I have used the following code for a stack implementation. The top keeps track of the topmost node of the stack. Now since top is a data member of the node function, each node created will have a top member, which ideally we wouldn't want.
- Is this good approach to coding?
- Will making
topasstaticmake it a better coding practice? - Should I have a global declaration of
top?
#include<iostream>
using namespace std;
class node
{
int data;
node *top;
node *link;
public:
node()
{
top=NULL;
link=NULL;
}
void push(int x)
{
node *n=new node;
n->data=x;
n->link=top;
top=n;
cout<<"Pushed "<<n->data<<endl;
}
void pop()
{
node *n=new node;
n=top;
top=top->link;
n->link=NULL;
cout<<"Popped "<<n->data<<endl;
delete n;
}
void print()
{
node *n=new node;
n=top;
while(n!=NULL)
{
cout<<n->data<<endl;
n=n->link;
}
delete n;
}
};
int main()
{
node stack;
stack.push(5);
stack.push(7);
stack.push(9);
stack.pop();
stack.print();
}
Any other suggestions welcome. I have also seen codes where there are two classes, where the second one has the top member. What about this?
4 Answers 4
In the
popand theprintfunction, thenew nodeseems unnecessary, since the next line modifies the pointer to point to thetop. It could be simplynode *n = top;The
delete n;also looks superfluous in theprintsincenwill always beNULLthere.If I'm right making
topas static or global would result that you can't have more than one stack in an application. Linked lists usually have two classes: one is aNodewhich stores a reference to the next node and the data, and the second is aLinkedListclass which stores the head and the implementation of thepush,pop, etc. methods. You can see an example in this answer.Take care of the boundary cases. Calling
popon an empty stack gives a segmentation fault error. I suppose that's not the best error handling method :)
-
1\$\begingroup\$ Thanks. I get that. :). What can I possibly do with
node *top? Should I create a new class calledlistor something and move thetopas a member of this class instead? \$\endgroup\$Quik Tester– Quik Tester2012年06月30日 15:38:58 +00:00Commented Jun 30, 2012 at 15:38 -
\$\begingroup\$ I think you should have two classes here, see the update please. \$\endgroup\$palacsint– palacsint2012年06月30日 15:43:30 +00:00Commented Jun 30, 2012 at 15:43
-
1\$\begingroup\$ Alright. :). I accept your solution. And thank you very much! \$\endgroup\$Quik Tester– Quik Tester2012年06月30日 16:16:00 +00:00Commented Jun 30, 2012 at 16:16
I'd normally structure the code more like this:
template <class T>
class stack {
class node {
T data;
node *next;
node(T const &i, node *n=NULL) : data(i), next(n) {}
};
node *top;
public:
void push(T const &in) {
top = new node(in, top);
}
stack() : top(NULL) {}
};
With this general structure, you get only one top per stack (instead of one in each node), but you can still create as many stack objects as you want (something that won't work if you make top a static or global).
I've left pop un-implemented for the moment because it's really somewhat tricky to get correct. In particular, the typical definition of pop that removes the top item from the stack and returns its value can't be made exception safe unless you place some fairly tight restrictions on the data that can be stored. The stack class in the standard library takes the route of separating the two actions -- one function to get the value of the top-most item, and a second to remove the top item from the stack.
It's not at all clear to me whether you'd really prefer to do that, or stick with a design that's easy to use, but may be unsafe.
I see a couple of problems with your print. First of all, it leaks memory -- it allocates an object, and attempts to delete it, but before the delete, the pointer has been set to NULL, so the delete is a nop. I think that's a direct consequence of the second problem: it's really quite a bit more complex than necessary. I'd normally use something closer to this:
void print() {
for (node *p=top; p!=NULL; p=p->next)
std::cout << p->data << "\n";
}
One more possibility to consider would be to implement the stack as a linked list of nodes, but have each node capable of holding a number of items instead of just one. This can reduce the overhead of the pointers quite a bit (e.g., as your code is written right now, in a typical implementation you have twice as much space devoted to pointers as to the data).
One alternative to having a separate class is to use the return value. For example:
Node * push(int x)
{
node *top = new node;
top->data = x;
top->link = this;
return top;
}
Then:
stack = stack->push(5);
stack = stack->push(7);
stack = stack->pop();
A separate stack class is probably easier to work with, but this is an option.
Additionally, a linked list is a poor choice of data structure for a stack. In almost all circumstances, a resizable array is a much better choice.
-
\$\begingroup\$ Good advice for C. Not for C++. \$\endgroup\$Loki Astari– Loki Astari2012年07月01日 22:29:46 +00:00Commented Jul 1, 2012 at 22:29
By the time I had this code together lots of people have made similar comments.
Anyway, here is my attempt at refactoring your code.
Main changes:
- templated type - more flexible.
- cleanup mechanism - destructor.
- general cleanup - lots of new where not required.
Looks like this:
#include<iostream>
using namespace std;
template<typename T>
class node
{
T data;
node *top;
node *link;
public:
node()
{
top=NULL;
link=NULL;
}
void push(const T& x)
{
node *n=new node;
n->data=x;
n->link=top;
top=n;
cout<<"Pushed "<<n->data<<endl;
}
void pop()
{
node *n=top;
top=top->link;
n->link=NULL;
cout<<"Popped "<<n->data<<endl;
delete n;
}
void print()
{
node *n= top;
while(n!=NULL)
{
cout<<n->data<<endl;
n=n->link;
}
}
~node() {
if(top) {
node* current = top;
node* next = 0;
while (current != NULL) {
next = current->link;
delete current;
current = next;
}
}
}
};
int main()
{
node<int> stack;
stack.push(5);
stack.push(7);
stack.push(9);
stack.pop();
stack.print();
return 0;
}
See @Loki's comment regarding The Rule of Three. In view of that here is my flawed second attempt:
#include<iostream>
using namespace std; //should not be in header file - just to remove std:: below
template <class T>
class stack {
class node {
public:
T data;
node* next;
node(T const &d) : data(d), next(0) {}
node() : data(0), next(0) {}
};
node *top;
public:
stack() : top(0) {}
stack(const stack& s) {
node* nextnode = s.top;
while(nextnode != 0)
{
node* newnode = new node;
newnode->data = nextnode->data;
newnode->next = nextnode->next;
nextnode=nextnode->next;
top = newnode;
cout<<"copy constructed "<< newnode->data <<endl;
}
}
stack& operator=(const stack& s) {
node* nextnode = s.top;
while(nextnode != 0)
{
node* newnode = new node;
newnode->data = nextnode->data;
newnode->next = nextnode->next;
top = newnode;
cout<<"operator = "<< newnode->data <<endl;
delete nextnode;
nextnode=nextnode->next;
}
}
~stack() {
if(top) {
node* current = top;
node* next = 0;
while (current != 0) {
next = current->next;
cout << "dtor deleting node " << current->data << endl;
delete current;
current = next;
}
}
}
void push(const T& item)
{
node *n=new node;
n->data=item;
n->next = top;
top=n;
cout<<"Push new'd node "<< n->data << endl;
}
void pop()
{
node *n=top;
top=top->next;
n->next=NULL;
cout<<"Pop delete'd node "<< n->data << endl;
delete n;
}
void print()
{
node *n= top;
while(n != 0)
{
cout<<n->data<<endl;
n=n->next;
}
}
};
int main()
{
{
stack<int> mystack;
mystack.push(5);
mystack.push(7);
mystack.push(9);
mystack.pop();
mystack.print();
//test of copy ctor
stack<int> second(mystack);
//test of assign operator
stack<int> third = mystack;
}
return 0;
}
-
\$\begingroup\$ Nice try. But this is not good. At a glance you fail the rule of three thus exposing yourself to double deletion and memory leaks all in one go. Try splitting into two classes
ListandNode. Then make sure the list implements rule of three. \$\endgroup\$Loki Astari– Loki Astari2012年06月30日 19:03:16 +00:00Commented Jun 30, 2012 at 19:03 -
\$\begingroup\$ @Loki - yes agreed. I just took posters code without really thinking too hard. I will spend time on it later on. \$\endgroup\$arcomber– arcomber2012年06月30日 19:30:46 +00:00Commented Jun 30, 2012 at 19:30
-
\$\begingroup\$ are you sure that your copy and assignment constructor work in correct way? Did you try to print, for example second.print() \$\endgroup\$German Petrov– German Petrov2015年12月28日 15:40:39 +00:00Commented Dec 28, 2015 at 15:40
topglobal or static means you can use only one stack in your program! You might want to distinguish between thenodeandlistclasses. \$\endgroup\$