Note that we're discussing code design questions, so it's not really about being right or wrong, but it's about what's recommended from experienced programmers and what has more pro than contra arguments.
I know a stack is a LIFO structure.
The author is declaring this functions:
void ChangeState(CGameState* state);
void PushState(CGameState* state);
void PopState();
That´s why he is using std::vector. If you only use the first one, of course you can use std::stack instead.
Could you then please enlight me where the vector does not get used in a LIFO manner?
The ChangeState function calls pop_back and push_back on the vector, where as the PushState calls push_back and the PopState calls pop_back. So all the operations happen on the last element of the vector and thus everything works with the LIFO principle.
Also keep in mind that the other of the tutorial stated himself, that he'll use the vector as a stack, so his idea and intend was to use the vector as a stack (LIFO) and not as something else. IMO he just didn't know the std::stack structur and thus didn't use it.
One doesn´t need to follow a tutorial strictly. The purpose of a tutorial is to illustrate something, IMO.
This counts for all tutorials, they are just there to help you understand a principle. But if you're intend is to use a stack, then don't use something else. Just providing a more generic structure, because maybe someone will want to use that isn't good and for beginners this can be quite confusing.
Not to forget that a stack is layed out for pushing and poping where as the vector isn't as efficent (as regarding memory) on such a task; of course it doesn't matter for this case, but it should just show that the decision of the right structur is very important and that one should not abuse other structurs for a diffrent task, if there's an existing one.
For example if you push twice the same state on the stack cleanup() would've not be called for the first state while for the second state already init() is being call
Can be easily fixed. If state to change == actual state, do nothing
If you mean by 'actual' 'current', then no you can't do that, because the state doesn't have to be the current state, but it could be paused deeper down in the stack. Of course you then could add a member to each state that tells if it's active or not, but this is then just a very bad design.
If you are careful with your pointers, you don´t have any memory leaks problem
This is actually just an excuse and a bad one too.
Memory management should, in most cases, be banned and one should make use of RAII with e.g.
std::share_ptr. Of course you don't have to take my word for that, but I think you could take
Bjarne Stroustrups' or
Herb Sutter's or any other's developer that likes to concentrate on safe code and good code design.
Additionally you can take care of your pointers as much as you want, but you'll never get the guarantee RAII gives you, since there are numerous ways to skip the cleanup code. For example a break statement, a return statement or a unexpected/unhandled exception, can easily jump over your cleanup code, where as objects with RAII get cleaned up as soon as it gets out of scope.
As an example take the render window, if you had to manually clean up the window the following code (as seen in many examples, but not very recommended for real applications) could create memory leaks:
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow window(sf::VideoMode(800, 600), "Leaky");
// window.init();
sf::Texture tex;
if(!tex.loadFromFile("test.png"))
return -1;
while(window.isOpen())
{
sf::Event event;
while(window.pollEvent(event))
{
if(event.type = sf::Event::Closed)
window.close()
}
window.clear();
window.display();
}
// window.cleanup();
}
I've commented two possible function calls you could have for manual memory management. As you see if the texture couldn't be loaded the application would automatically close (return -1;) without calling window.cleanup() which will create memory leaks. Of course you then wouldn't use return -1 but one of the common design guidlines is to design your code so that it's very hard for the user to do things wrong and manual memory management certaintly doesn't follow this rule.
This is also why SFML is so awesome and other libraries like SDL, Allegro, etc. have some bad design issues.