Stack implementation in C++ including min(), max() functions in O(1) This implementation seems to work. I would love to get any suggestions / comments on how this code could be improved. Are there any potential memory management issues? Any additional methods that would be useful (also, from educational point of view) to implement? Thank you!

Stack.hpp

#ifndef STACK_H
#define STACK_H

#include &lt;iostream&gt;
#include &lt;memory&gt;
#include &lt;vector&gt;
#include &lt;stdexcept&gt;

class Stack {

private:
    struct Node {
        int val;
        // To get min and max value of stack in O(1):
        int maxVal = std::numeric_limits&lt;int&gt;::min();
        int minVal = std::numeric_limits&lt;int&gt;::max();
    };

    std::vector&lt;Node&gt; currStack;

public:

    int size() const { return currStack.size(); }
    bool empty() const { return currStack.empty(); }
    void pop() { currStack.pop_back(); }

    void push(int val);
    int top() const; // O(1)
    int min() const; // O(1)
    int max() const; // O(1)

};

#endif /* STACK_H */


Stack.cpp

#include "Stack.hpp"

void Stack::push(int val) {

    Node tempNode;
    tempNode.val = val;
    if (this-&gt;empty()) {
        tempNode.minVal = val;
        tempNode.maxVal = val;
    } else {
        tempNode.minVal = std::min(currStack.back().minVal, val);
        tempNode.maxVal = std::max(currStack.back().maxVal, val);
    }

    currStack.push_back(tempNode);
}

int Stack::top() const {
    if (currStack.empty()) {
        throw std::out_of_range ("Stack is empty");
    }
    return currStack.back().val;
}

int Stack::min() const {
    if (currStack.empty()) {
        throw std::out_of_range ("Stack is empty");
    }
    return currStack.back().minVal;
}

int Stack::max() const {
    if (currStack.empty()) {
        throw std::out_of_range ("Stack is empty");
    }
    return currStack.back().maxVal;
}


main.cpp

#include "Stack.hpp"

int main() {

    // Stack init
    Stack myStack;

    std::cout &lt;&lt; "Empty? " &lt;&lt; myStack.empty() &lt;&lt; "\n";
    std::cout &lt;&lt; "Size: " &lt;&lt; myStack.size() &lt;&lt; "\n";

    try {
        std::cout &lt;&lt; "Top: " &lt;&lt; myStack.top() &lt;&lt; "\n";
        std::cout &lt;&lt; "Min: " &lt;&lt; myStack.min() &lt;&lt; "\n";
        std::cout &lt;&lt; "Max: " &lt;&lt; myStack.max() &lt;&lt; "\n";
    }
    catch (const std::out_of_range&amp; e) {
        std::cout &lt;&lt; "Out of range error. " &lt;&lt; e.what() &lt;&lt; "\n";
    }

    std::cout &lt;&lt; "Push some integers: \n";
    myStack.push(5);
    myStack.push(3);
    myStack.push(7);

    std::cout &lt;&lt; "Empty? " &lt;&lt; myStack.empty() &lt;&lt; "\n";
    std::cout &lt;&lt; "Size: " &lt;&lt; myStack.size() &lt;&lt; "\n";

    try {
        std::cout &lt;&lt; "Top: " &lt;&lt; myStack.top() &lt;&lt; "\n";
        std::cout &lt;&lt; "Min: " &lt;&lt; myStack.min() &lt;&lt; "\n";
        std::cout &lt;&lt; "Max: " &lt;&lt; myStack.max() &lt;&lt; "\n";
    }
    catch (const std::out_of_range&amp; e) {
        std::cout &lt;&lt; "Out of range error. " &lt;&lt; e.what() &lt;&lt; "\n";
    }

    std::cout &lt;&lt; "Pop some integers: \n";
    myStack.pop();

    std::cout &lt;&lt; "Empty? " &lt;&lt; myStack.empty() &lt;&lt; "\n";
    std::cout &lt;&lt; "Size: " &lt;&lt; myStack.size() &lt;&lt; "\n";

    try {
        std::cout &lt;&lt; "Top: " &lt;&lt; myStack.top() &lt;&lt; "\n";
        std::cout &lt;&lt; "Min: " &lt;&lt; myStack.min() &lt;&lt; "\n";
        std::cout &lt;&lt; "Max: " &lt;&lt; myStack.max() &lt;&lt; "\n";
    }
    catch (const std::out_of_range&amp; e) {
        std::cout &lt;&lt; "Out of range error. " &lt;&lt; e.what() &lt;&lt; "\n";
    }
}
I'm going to answer your direct questions first.


  Are there any potential memory management issues?


No. You don't do any manual memory management (using new and delete) so there are no issues.


  Any additional methods that would be useful (also, from educational
  point of view) to implement?


Here is the reference page for std::stack. This is the standard library documentation. You may not be able to do them all right now but those are all possible functions and members you could implement for a stack. (I recommend only trying a little bit at a time.

I'd like to point out that your code is very clean and easy to read. I also appreciate good use of const correctness.



Use unique include guards

#ifndef STACK_H
#define STACK_H


This is a very basic name. It would be easy for others to use a very similar name. Make your includes unique. Add a namespace name, a project name, a library name and possibly a GUID or all of the above to eliminate the risk of clashes.



Use the right headers

#include &lt;iostream&gt;
#include &lt;memory&gt;
#include &lt;vector&gt;
#include &lt;stdexcept&gt;


You don't use &lt;memory&gt; or &lt;iostream&gt; in your class. You don't use &lt;stdexcept&gt; until the cpp file. declare the header there.

You are missing &lt;limits&gt; for :std::numeric_limits&lt;&gt;



List public members first

Anyone using your stack is only going to be able to use the public member functions. It is common practice therefor to list these first. 



Avoid the use of this-&gt;

It is almost always wrong to have this-&gt; in your code in C++. For example this:

if (this-&gt;empty())


can simply be written:

if (empty()) 


The exception to this rule is with variable shadowing (which you don't have) which is also typically solved with a name change.



Always ask: What does this feature serve?

So you implemented a min() and max(). You did it with an O(1) lookup by having every single element keep track of the current min and max value of the entire stack. This is a problem on two levels:


Your nodes should not contain information on other nodes. That breaks encapsulation. This is the job of the Container.
And its also useless information. There is one accessible node. What value do you gain by knowing what the min and max values beneath that one are? how can you even act on that even if you knew?


If you have your heart content on keeping the min() max() then at least store the value in the container. In addition to being good practice it will also take significantly less memory.



Lastly When you are ready you should look into Template Metaprogramming. A Container / Data Structure like this needs to be usable with any type, including user defined types, in order to be truly useful/robust. 



Edit: to answer point brought up in comments

The problem as I see it is the fact that you need to store state, which a stack is great for. If I needed to maintain current, min, and max values like you are, I would put them all into a struct like so:

struct State
{
     int current_value;
     int min_value;
     int max_value;
}


Then I would use a stack that supports user-defined types. std::stack&lt;State&gt; state_machine. 

Furthermore by maintaining min() and max(), in the node or in the container, if you do decide to extend your container to support user-defined types you will force your users to overload the comparison operators for their type even if there is no logical way to do so and it isn't otherwise needed.