class templates

I am trying to make my own stack class as a beginning exercise, but so far I am stuck as to why mu IDE 'doesn't like' what code? If I put the functions in with the declarations in the .hpp file all is good, but if I try put them into a separate .cpp file I get several errors.

Thanks in advance...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
 #ifndef stack_hpp
#define stack_hpp
#include <iostream>

template<typename T, int SZ = 20>
class stack
{
private:
    T data[SZ];
    int top{};
public:
    void push(T x)
    T pop()
    T peek() const
    
    friend std::ostream& operator<<(std::ostream& out , const stack& s)
    {
        out <<"[ ";
        for(int i{};i < s.top; ++i)
        {
            out << s.data[i] << " ";
        }
        out << "]";
        return out;
    }
};

#endif /* stack_hpp */

#include "stack.hpp"

template<typename T>
void stack<T>::push(T x)
{
    data[top++] = x;
}
 
template<typename T>
T stack<T>::pop()
{
    return data[--top];
}
 
template<typename T>
T stack<T>::peek() const
{
    return data[top - 1];
}

Last edited on
but if I try put them into a separate .cpp file I get several errors.

Templates need to be put in the header files so that the compiler can see the full definition of class/function templates in order to generate the correct classes/functions.
Last edited on
Ok. Thanks. So that means I can’t split my class up as I have previously into two files ie stack.hpp then stack.cpp. Everything must go into the stack.hpp file.
I found out through trial and error that it was the only way to get m6 code to compile.

Thanks again
This is what I have so far for my stack class. Part of the exercise is to add exceptions when trying to access over or under the stack. The size of my stack is default 2 to test my exceptions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
template<typename T, int SZ = 2>
class stack
{
private:
    T data[SZ];
    int top{};
public:
    void push(T x)
    {
        if(isFull())
        {
            data[top++] = x;
        }
        else
        {
            throw overflow(top);
        }
        
    }
    
    T pop()
    {
        if(!isEmpty())
        {
            throw underflow(top);
        }
        else
        return data[--top];
    }
    T peek() const
    {
        if(top == 0)
        {
            throw underflow(top);
        }
        else
        return data[top - 1];
    }
    
    bool isEmpty()
    {
        return top > 0;
    }
    
    bool isFull()
    {
        return top < SZ;
    }
    
    friend std::ostream& operator<<(std::ostream& out , const stack& s)
    {
        out <<"[ ";
        for(int i{};i < s.top; ++i)
        {
            out << s.data[i] << " ";
        }
        out << "]";
        return out;
    }
};

#endif /* stack_hpp */

int main(int argc, const char * argv[]) {
    
    stack<float> sf;
    
    try
    {
        sf.peek();
    }
    catch(const underflow& ex)
    {
        std::cout << ex.what() << std::endl;
    }
    try
    {
        sf.push(3.4);
        std::cout << sf << std::endl;
        sf.push(5.3);
        std::cout << sf << std::endl;
        sf.push(6.2);
        std::cout << sf << std::endl;
    }
    catch(const overflow& ex)
    {
        std::cout << ex.what() << std::endl;
    }
    try
    {
        std::cout << sf.pop() << std::endl;
        std::cout << sf.pop() << std::endl;
        std::cout << sf.pop() << std::endl;
    }
    catch(const underflow& ex)
    {
        std::cout << ex.what() << std::endl;
    }
    
    return 0;
}



Last edited on
An exception happens under exceptional circumstances. I believe you have the logic backwards in your pop() and push() functions.
If you try to push() to the stack when you don't have enough room to do so, that's the place to throw an exception.
Likewise, if you try to pop off the stack when the stack is empty, that's another place to throw an exception.

Did you have more specific concerns? I didn't actually run your code since it's only an excerpt.
my isFull() returns true as long as it is less my variable top, therefore it 'should' only push to the stack when there is room.
my isEmpty() is true as long as top > 0, that's why I have used if(!isEmpty) so I can return a value from pop().

I was wondering if there is a 'better' way or more elegant way to go about this? As is my code does work. You don't think I should throw an exception when either over or under the stack?
To anyone reading code, "is full" means "there is no more room left, you can't push more", so I think your naming scheme is unnecessarily confusing, regardless of if the underlying implementation of the function is correct.

Your code seems fine from what I can tell, aside from what I said above.
- peeking into empty stack makes no sense --> exception is thrown
- pushing to a full stack is not allowed --> exception is thrown
- popping off an empty stack is not allowed --> exception is thrown
Last edited on
Thanks! Yes I think I should change my isFull() and isEmpty to something that makes more sense. Thanks again. I seem to be getting somewhere...!
I think the minor changes make more sense

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

template<typename T, int SZ = 2>
class stack
{
private:
    T data[SZ];
    int top{};
public:
    void push(T x)
    {
        if(stack_not_Full())
        {
            data[top++] = x;
        }
        else
        {
            throw overflow(top);
        }
    }
    
    T pop()
    {
        if(stack_not_Empty())
        {
            return data[--top];
        }
        else
        throw underflow(top);
    }
    T peek() const
    {
        if(top == 0)
        {
            throw underflow(top);
        }
        else
        return data[top - 1];
    }
    
    bool stack_not_Empty()
    {
        return top > 0;
    }
    
    bool stack_not_Full()
    {
        return top < SZ;
    }
    
    friend std::ostream& operator<<(std::ostream& out , const stack& s)
    {
        out <<"[ ";
        for(int i{};i < s.top; ++i)
        {
            out << s.data[i] << " ";
        }
        out << "]";
        return out;
    }
};




My next project is to build on my stack experience and implement a circular buffer class. Where as the the stack is last in first out, it is my understanding that a circular buffer is first in first out.
I will need an index for the push function to put data in the buffer then make sure the index flips over to the start of the buffer.
If I make a 10 element buffer and write 11 pieces of data then the next first out is not at position 0 but position 1.
I will have a think and see what I can come up with...

Thanks in advance!!

James
Topic archived. No new replies allowed.