1

I've recently taken a course in Data Structures in Java and I can write various structures and implement them in various ways in Java easily. I'm currently transferring this knowledge to the world of C++ which is a little different. I currently wrote a header file for a Stack interface (like you would writing an interface in Java for Stack) and I want to implement it in various ways (linked lists, array, vector, etc.) so I can master the idea of implementing structures in any language. The problem I'm currently having with C++ is understanding the notion of using pointers in my Stack and references (E&) plus making sure I can write various source implementations of Stack.h. Here is my current stack header...

/*
 * File: stack.h
 * -------------
 * Interface file of the Stack class. Follows a last-in/ first-out (LIFO) order. Includes contracts for the method bodies of Stack.
*/

#ifndef _stack_h
#define _stack_h

/*
 * declaration of Stack using template with type E for any data type or object using the Stack structure.
 *
 */

  template <typename E>
  class Stack {

    public: 

    //constructor blank
    Stack();

    //destructor
    ~Stack();

    //constructor with integer size
    Stack(int);

    //number of items in stack.
    int size() const;

    //is stack empty?
    bool empty() const;

    //top element of stack
    const E& top() const throw(StackEmpty);

    //push e onto the stack.
    void push(const E& e);

    //remove top element from stack
    void pop() throw(StackEmpty);

    //pop and return.
    E popReturn();

    //returns a string form of stack.
    std::string toString();
};

// Error exception
class StackEmpty : public RuntimeException {
    public:
       StackEmpty(const std::string& err) : RuntimeException(err) {}
};

#endif

Sorry for the formatting! :) Currently I'm working on the Array and Linked List implementation of this stack. I understand that a header file creates a link between files it is included in. I want to insure that when I create a stack for testing I can use both implementations I wrote with this header. I'm also not sure if I should use the keyword virtual or not to make an official interface. I know in java when you declare an implementation for Stack you would use

Stack test = new ArrayStack();

Is that the same for C++ to use a globalized header file and use different implementations of this Stack? Also this code is burrowed from a Data Structures book in C++ but sadly the authors do not say to make these interfaces a header file or not, and where to include that error checking exception for an empty stack. I just placed it in this file. C++ hasn't been a great language to me but I know that if I wanna build larger projects like Compilers, games, audio/ video players, OS, and writing an IDE for a new language that this and C are important to master. Please give me any insight to my current situation if possible I would greatly appreciate it. If anyone also can explain pointers and references in C++ I would greatly accept the knowledge. I believe the E& is a reference but the book didn't specify. Thank you! :)

P.S. this is what I thought would work for C++ for using different implementations of a header....

#include "stack.h"

Stack test = new ArrayStack();
Stack test2 = new LinkedStack();
Cory Kramer
  • 98,167
  • 13
  • 130
  • 181
MD_90
  • 5
  • 1
  • 6
  • 5
    Step 1 moving from Java to C++: Stop `new`ing everything https://stackoverflow.com/questions/6500313/why-should-c-programmers-minimize-use-of-new – Cory Kramer Jun 16 '15 at 20:03
  • 1
    You are certainly going to have to break the habit of using `new` as this is something that you generally speaking don't want to use very often. The times where you use `new` in Java are often more easily handled in c++ by passing thing by value. `new` in c++ creates a pointer which is a different type than just your standard constructor object creation. – shuttle87 Jun 16 '15 at 20:04
  • When you `new` (which is not very necessary in modern C++), you must assign the result to a pointer variable (e.g. `Stack *test =`). – crashmstr Jun 16 '15 at 20:05
  • Also `e&` is a reference type in the context of this question. `void push(const E& e);` is an example of the pass-by-reference-to-const idiom in case you need search keywords. – shuttle87 Jun 16 '15 at 20:07
  • You could make a non-const version of top(), you may want to change the "top" of the stack without having to pop and push again. – aslg Jun 16 '15 at 20:08
  • 3
    Try to learn C++ from [a C++ book](http://stackoverflow.com/q/388242/560648) rather than from a combination of Java and Stack Overflow. :) – Lightness Races in Orbit Jun 16 '15 at 20:19
  • so how do you write different implementations for this Stack.h file? I want still want to use this header. – MD_90 Jun 16 '15 at 20:30
  • @MD_90 You write different implementations, and you compile with different `cpp` files. This is usually why people compile with both `h` and `cpp` files--by separating them, you're given the freedom to swap different implementations out and still have them "work" if they are implemented to a certain interface's spec. – CinchBlue Jun 16 '15 at 20:38
  • Unless there's something I don't know you have to define your templates in the header file.. – aslg Jun 16 '15 at 20:39
  • oh okay so instead of using a generic header for a global Stack you would have like an ArrayStack.h then its implementation ArrayStack.cpp instead of Stack.h and ArrayStack.cpp and LinkedStack.cpp both implementating Stack.h? – MD_90 Jun 16 '15 at 20:42
  • @MD_90 Yes, normally you separate the declaration (.h) from the implementation (.cpp). But for templates it doesn't work that way and you can see why here http://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file – aslg Jun 16 '15 at 20:49

2 Answers2

2

Your code has a number of problems.

template <typename E>

Unless you honestly need run-time polymorphism (which you almost certainly don't in this case) you want to pass the underlying storage you're going to use as a template parameter instead of using inheritance:

template <typename E, typename Container = std::vector<E> >

This way, you can avoid (for example) virtual function calls for elementary operations that really don't need it.

//constructor blank
Stack();

You probably don't need to declare this default constructor at all. (See below in the comments about the other constructor you declared).

//destructor
~Stack();

If you're going to use Stack as a base class as you seem to be suggesting, then you want to make the destructor virtual.

//constructor with integer size
Stack(int);

Unless you really want to use an underlying container with a fixed size, there's no real need to specify the size here. If you want to specify a size anyway, I'd probably provide a default value (which is part of what renders the preceding default constructor declaration irrelevant) so you end up with something like this: Stack(int size = 20);

//top element of stack
const E& top() const throw(StackEmpty);

This is bad. Dynamic exception specifications like your throw(StackEmpty) turned out to be a mistake. They've been deprecated for a while now, and will probably disappear soon1. In addition, if you're going to use inheritance, most of these interface functions should probably be virtual (probably pure-virtual).

//push e onto the stack.
void push(const E& e);

This is all right, but if you want to get the most out of C++, you probably want to add an overload that takes an rvalue reference as well.

//remove top element from stack
void pop() throw(StackEmpty);

This dynamic exception specification has the same problem as above.

//pop and return.
E popReturn();

This has a serious design flaw--if E's copy constructor throws an exception, it will (unavoidably) destroy data. There are two fairly well-known ways to avoid this problem. One is to just eliminate it and require the user to write code like:

Foo f = myStack.top();
myStack.pop();

This way, if the copy constructor throws, myStack.pop() never executes. The other well known possibility is something like:

void pop(E &dest) { 
    dest = top();
    pop();
}

Either way, we end up with exception safe code--either it completes (and the value is transferred from the top of the stack to the destination) or else it fails completely (and the data remains on the stack), so there was no effect.

//returns a string form of stack.
std::string toString();

In C++ this is normally named to_string.

// Error exception
class StackEmpty : public RuntimeException {
    public:
       StackEmpty(const std::string& err) : RuntimeException(err) {}
};

The standard already defines std::runtime_error. It's probably better to use that until/unless you know C++ well enough to know exactly what you want to do differently.

Also note: if you take the initial advice and make your stack a pure template instead of trying to use inheritance, many of the other comments become moot.

In the end, a stack class for C++ can end up more like this:

template <class T, class C = std::vector<T> >
class Stack { 
    C data;
public:
    void push(T const &t) { 
        data.push_back(t);
    }

    T top() const {  return data.back(); }

    void pop(T &d) { 
        if (data.empty())
            throw std::runtime_error("Attempt to pop empty stack");
        d = data.top();
        data.pop_back();
    }

    bool empty() const { return data.empty(); }
};

You've indicated that you want to test it with different underlying containers. Here's how you'd do it with three standard containers:

#include "stack.h"
#include <vector>
#include <list>
#include <deque>
...
// These two are equivalent:
Stack<int, std::vector<int>> vs;
Stack<int> vs1;

Stack<int, std::list<int>> ls;  
Stack<int, std::deque<int>> ds;

1. Yes, Java copied this mistake almost intact, then managed to actually make it even worse by adding tight coupling, which destroys one of the biggest advantages of exception handling to start with. About all I can say is: that hurts; don't do it any more.

Jerry Coffin
  • 437,173
  • 71
  • 570
  • 1,035
  • Thank you Jerry! If I want to implement two different versions of this Stack file what do you suggest is best approach? I saw some code online from Stanford that writes implementations of stack in the stack header file – MD_90 Jun 16 '15 at 20:54
  • @JerryCoffin: I believe `top()` should be `const` too. Or am I mistaken? – yzt Jun 16 '15 at 21:11
  • Whats the meaning for the class C = std::vector in the declaration? what about an object array or linked list? – MD_90 Jun 16 '15 at 21:22
  • @JerryCoffin: I know this is OT, but I suggest removing the reserving constructor. It precludes the use of all standard containers except `std::vector`, since the other containers (list, deque, etc.) don't have a `reserve` method, IIRC. – yzt Jun 16 '15 at 21:27
  • @JerryCoffin: I took the liberty of adding a usage example in your answer (great answer BTW; thanks for that!) Obviously feel free to edit/remove. – yzt Jun 16 '15 at 21:31
1

I'm not going to comment on the quality, usability, correctness, or "C++-ness" of that stack. To any (C++) programmer, it'd be painfully obvious that a Java programmer wrote that stack class, which might be fine in Java, but there are far better ways in C++.

What I'm going to do is try and answer your question about interfaces and using them, in relation to include files. (Again, let me emphasize that the stack class itself is not good.)

What you might want to do is have a "stack.h", with content similar to this:

template <typename E>
class Stack
{
public:
    // These are *usually* useless in an abstract interface 
    // Stack();
    // ~Stack();
    // Stack(int);

    // Every concrete class inheriting from this class must implement all these:
    virtual int size() const = 0;
    virtual bool empty() const = 0;
    virtual const E& top() const throw(StackEmpty) = 0;
    virtual void push(const E& e) = 0;
    virtual void pop() throw(StackEmpty) = 0;
    virtual E popReturn() = 0;
};

Now, you might have a "vector_stack.h" like this:

#include "stack.h"

template <typename E>
class VectorStack
    : public Stack<E>
{
public:
    VectorStack() {...}
    virtual ~VectorStack() {...}
    Virtual VectorStack(int) {...}

    virtual int size() const override {...}
    virtual bool empty() const override {...}
    virtual const E& top() const throw(StackEmpty) override {...}
    virtual void push(const E& e) override {...}
    virtual void pop() throw(StackEmpty) override {...}
    virtual E popReturn() override {...}
};

You'd do the exact same thing with any other implementation you want to do.

Now, in the code file that you want to use all these, you do this:

#include "VectorStack.h"
#include "ListStack.h"
#include "WhateverStack.h"

...
// I apologize to all C++ programmers, for not using unique_ptr<>!
Stack<int> * s1 = new VectorStack<int>;
Stack<int> * s2 = new ListStack<int>;
Stack<int> * s3 = new WhateverStack<int>;
...
...
// Don't forget to delete if you use raw pointers; you're
// not in Kansas anymore!
delete s3;
delete s2;
delete s1;

But this is not very "C++", not to mention flexible or efficient. Specially in this case, for containers like a stack. But if you insist on that interface (in the general sense,) this is probably what you have to do.

Oh, and don't forget to #include the headers that you use, such as <string>. And note that if you want those exception specifications (don't do it!) the declaration for your exception class must go before the declaration of your class.

(By the way, I'm pretty sure it's the popReturn method that throws, not pop.)

yzt
  • 7,898
  • 1
  • 31
  • 41
  • @MD_90: See [Jerry Coffin's answer above](http://stackoverflow.com/a/30877751/383045) for that. But that would probably require more C++ proficiency than you have. (I absolutely mean no offense; I'm merely inferring your level of C++ from your question, and obviously I could be mistaken.) – yzt Jun 16 '15 at 21:04
  • My C++ experience isn't perfect. I've been learning by this data structures book and cplusplus.com – MD_90 Jun 16 '15 at 21:20
  • @MD_90: Hey, nothing wrong with that. We're all learning. I'm just worried that a more efficient, beautiful, "cplusplusy" implementation at this point would mire you in problems that would stop you from experimenting and learning. – yzt Jun 16 '15 at 21:38