Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com
Answertopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions
Privacy Policy

  




 

 

Thinking in C++ Vol 2 - Practical Programming
Prev Home Next

stack

The stack container, along with queue and priority_queue, are classified as adaptors, which means they adapt one of the basic sequence containers to store their data. This is an unfortunate case of confusing what something does with the details of its underlying implementation the fact that these are called adaptors is of primary value only to the creator of the library. When you use them, you generally don t care that they re adaptors, but instead that they solve your problem. Admittedly it s useful at times to know that you can choose an alternate implementation or build an adaptor from an existing container object, but that s generally one level removed from the adaptor s behavior. So, while you may see it emphasized elsewhere that a particular container is an adaptor, we ll only point out that fact when it s useful. Note that each type of adaptor has a default container that it s built upon, and this default is the most sensible implementation. In most cases you won t need to concern yourself with the underlying implementation.

The following example shows stack<string> implemented in the three ways: the default (which uses deque), then with a vector, and finally with a list:

//: C07:Stack1.cpp
// Demonstrates the STL stack.
#include <fstream>
#include <iostream>
#include <list>
#include <stack>
#include <string>
#include <vector>
using namespace std;
 
// Rearrange comments below to use different versions.
typedef stack<string> Stack1; // Default: deque<string>
// typedef stack<string, vector<string> > Stack2;
// typedef stack<string, list<string> > Stack3;
 
int main() {
ifstream in("Stack1.cpp");
Stack1 textlines; // Try the different versions
// Read file and store lines in the stack:
string line;
while(getline(in, line))
textlines.push(line + "\n");
// Print lines from the stack and pop them:
while(!textlines.empty()) {
cout << textlines.top();
textlines.pop();
}
} ///:~
 

The top( ) and pop( ) operations will probably seem non-intuitive if you ve used other stack classes. When you call pop( ), it returns void rather than the top element that you might have expected. If you want the top element, you get a reference to it with top( ). It turns out this is more efficient, since a traditional pop( ) must return a value rather than a reference and thus invokes the copy-constructor. More important, it is exception safe, as we discussed in Chapter 1. If pop( ) both changed the state of the stack and attempted to return the top element, an exception in the element s copy-constructor could cause the element to be lost. When you re using a stack (or a priority_queue, described later), you can efficiently refer to top( ) as many times as you want and then discard the top element explicitly using pop( ). (Perhaps if some term other than the familiar pop had been used, this would have been a bit clearer.)

The stack template has a simple interface essentially the member functions you saw earlier. Since it only makes sense to access a stack at its top, no iterators are available for traversing it. Nor are there sophisticated forms of initialization, but if you need that, you can use the underlying container upon which the stack is implemented. For example, suppose you have a function that expects a stack interface, but in the rest of your program you need the objects stored in a list. The following program stores each line of a file along with the leading number of spaces in that line. (You might imagine it as a starting point for performing some kind of source-code reformatting.)

//: C07:Stack2.cpp
// Converting a list to a stack.
#include <iostream>
#include <fstream>
#include <stack>
#include <list>
#include <string>
#include <cstddef>
using namespace std;
 
// Expects a stack:
template<class Stk>
void stackOut(Stk& s, ostream& os = cout) {
while(!s.empty()) {
os << s.top() << "\n";
s.pop();
}
}
 
class Line {
string line; // Without leading spaces
size_t lspaces; // Number of leading spaces
public:
Line(string s) : line(s) {
lspaces = line.find_first_not_of(' ');
if(lspaces == string::npos)
lspaces = 0;
line = line.substr(lspaces);
}
friend ostream& operator<<(ostream& os, const Line& l) {
for(size_t i = 0; i < l.lspaces; i++)
os << ' ';
return os << l.line;
}
// Other functions here...
};
 
int main() {
ifstream in("Stack2.cpp");
list<Line> lines;
// Read file and store lines in the list:
string s;
while(getline(in, s))
lines.push_front(s);
// Turn the list into a stack for printing:
stack<Line, list<Line> > stk(lines);
stackOut(stk);
} ///:~
 

The function that requires the stack interface just sends each top( ) object to an ostream and then removes it by calling pop( ). The Line class determines the number of leading spaces and then stores the contents of the line without the leading spaces. The ostream operator<< re-inserts the leading spaces so the line prints properly, but you can easily change the number of spaces by changing the value of lspaces. (The member functions to do this are not shown here.) In main( ), the input file is read into a list<Line>, and then each line in the list is copied into a stack that is sent to stackOut( ).

You cannot iterate through a stack; this emphasizes that you only want to perform stack operations when you create a stack. You can get equivalent stack functionality using a vector and its back( ), push_back( ), and pop_back( ) member functions, and then you have all the additional functionality of the vector. The program Stack1.cpp can be rewritten to show this:

//: C07:Stack3.cpp
// Using a vector as a stack; modified Stack1.cpp.
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
 
int main() {
ifstream in("Stack3.cpp");
vector<string> textlines;
string line;
while(getline(in, line))
textlines.push_back(line + "\n");
while(!textlines.empty()) {
cout << textlines.back();
textlines.pop_back();
}
} ///:~
 

This produces the same output as Stack1.cpp, but you can now perform vector operations as well. A list can also push things at the front, but it s generally less efficient than using push_back( ) with vector. (In addition, deque is usually more efficient than list for pushing things at the front.)

Thinking in C++ Vol 2 - Practical Programming
Prev Home Next

 
 
   Reproduced courtesy of Bruce Eckel, MindView, Inc. Design by Interspire