Why is destructor getting called

Started by
7 comments, last by rip-off 12 years, 6 months ago
I have a custom class lets call it MyClass

so in my program I have this


class Game.h
{
MyClass my_class_instance;

void Init();
void Run();
}

class Game.cpp
{
void Game::Init()
{
my_class_instance = MyClass(constructor_params); // destructor getting called here
}

void Game::Run()
{
my_class_instance.DoSomething(); // instance still valid here
}

}
Advertisement
my_class_instance = MyClass(constructor_params);
First you create a temporary MyClass object with MyClass(constructor_params). Then you assign this object to my_class_instance using operator=. The temporary object will not exist after this line so the destructor is called.

my_class_instance = MyClass(constructor_params);
First you create a temporary MyClass object with MyClass(constructor_params). Then you assign this object to my_class_instance using operator=. The temporary object will not exist after this line so the destructor is called.


Oh ok but even if I do it like this

my_class_instance(constructor_params);

the destructor gets called why?
post some real code

[quote name='Wooh' timestamp='1317959501' post='4869973']
my_class_instance = MyClass(constructor_params);
First you create a temporary MyClass object with MyClass(constructor_params). Then you assign this object to my_class_instance using operator=. The temporary object will not exist after this line so the destructor is called.


Oh ok but even if I do it like this

my_class_instance(constructor_params);

the destructor gets called why?
[/quote]

Probably because it goes out of scope, the = operator should perform a shallow copy of the instance in your case.

Thus you basically have:

MyClass my_class_instance; (from game.h) , this creates one instance of MyClass using the default constructor and it remains valid until the Game instance is destroyed.

Then you do:

my_class_instance = MyClass(parameters); this creates a second temporary instance of MyClass, copies its values to the instance you have in the Game class and then destroys the temporary instance (Which is why the destructor gets called).
[size="1"]I don't suffer from insanity, I'm enjoying every minute of it.
The voices in my head may not be real, but they have some good ideas!
I think that what you are doing and what you are trying to do are two different things. I believe that you are confusing dynamic and static instantiation.

If I am correct in assuming this, the best thing to do in this situation would be to use a pointer.

Changing your code, we have:

Game.h

[color=#1C2837][size=2]class Game
{
MyClass* my_class_instance;

void Init();
void Run();
};
[color="#1c2837"]

[color="#1c2837"]

[color="#1c2837"]Game.cpp
[color="#1c2837"]
[color=#1C2837][size=2]void Game::Init()
{
my_class_instance = new MyClass(constructor_params);
}

void Game::Run()
{
my_class_instance->DoSomething();
}
[color="#1c2837"]
Co-founder/Lead Programmer
Bonafide Software, L.L.C.
Fairmont, WV 26554 US
Think of it this way: A class initialises all of its members when its constructor is called - and then it runs the constructor body.
So if you have
[source lang="cpp"]
class Game
{
MyClass my_class_instance;
void Init() { my_class_instance = MyClass(params); }
}
[/source]

Game will:
1) create my_class_instance() using the () constructor.
2) at Init(), create a temporary MyClass(params), copy it into my_class_instance using the = operator, then delete it.

Consider instead, if you have a constructor:
[source lang="cpp"]
class Game
{
MyClass my_class_instance;
Game(params) : my_class_instance(params) { }
}

[/source]

then my_class_instance will be constructed using the (params) constructor. This is important, since the following code does not do the same thing, but is instead equivalent to the first version:

[source lang="cpp"]
class Game
{
MyClass my_class_instance;
Game(params) { my_class_instance = MyClass(params); }
}

[/source]

If you really, really want to construct the instance in Init, and in Init only, then you probably want to make it a pointer:
[source lang="cpp"]
class Game
{
MyClass *my_class_instance;
Game() : my_class_instance(NULL) { }
void Init() { my_class_instance = new MyClass(params); }
~Game() { delete my_class_instance; }
}

[/source]

[source lang="c++"]
class Game.cpp
{
MyClass my_class_instance;
...
};

void Game::Init()
{
my_class_instance = MyClass(constructor_params); // destructor getting called here
}
[/source]

Variables declared like this (or like my_class_instance( constructor_params )) are created on the stack and get destroyed when they go out of scope. In this case, the variable's scope is the method Game::Init(), so at the end of the function the variable gets destroyed. If you want to keep the variable around for a while, you should use a pointer. So:
[source lang="c++"]
class Game.h
{
public:
Game();
~Game();
void Init();
void Run();

private:
MyClass* my_class_instance;
}

Game::~Game()
{
delete my_class_instance;
my_class_instance = 0;
}

void Game::Init()
{
my_class_instance = new MyClass(constructor_params);
}
[/source]
By using new, the variable is created on the heap, which is a different part of memory than the stack, and it doesn't get cleaned up automatically. Note that with this method, you have to destroy the object yourself by calling delete my_class_instance at some point, often from the class's destructor, as I've done above. Pointers can be tricky, and many people will tell you to avoid them when possible, and to use a smart pointer when you must use one at all. Smart pointers are well worth looking into and learning how to use.

However, I think the best thing to do in this case is to use an initializer list:
[source lang="c=="]
class Game.h
{
public:
Game();
~Game();
void Run();

private:
MyClass my_class_instance;
}

Game::Game()
: my_class_instance( constructor_params )
{ }
[/source]
It's cleaner this way--my_class_instance gets created at the time the Game object is created and lasts the lifetime of its parent class, and you don't need to remember to delete it. It's not always possible to use initializer lists for all class variables, but you should prefer it whenever you can.
In C++, when you are designing a complex class (anything that isn't POD), you should ask yourself, how will I handle the Rule of Three?

The simplest answer is to make all complex classes "noncopyable" by default, and when you find that this is causing your trouble in the code, then you can decide if a copy is necessary, whether to pass a (smart) reference or decide what it would mean to copy such an object.

Marking a class as noncopyable is as simple as the following (which is essentially boost::noncopyable if you're using boost):

class noncopyable
{
protected:
noncopyable()
{
}
private:
// Note: these are deliberately private and unimplemented: do not copy instances of this class!
noncopyable(const noncopyable &);
noncopyable &operator=(const noncopyable &);
};

class Fancy : noncopyable
{
// ...
};

When you make classes noncopyable, you can concentrate on writing the correct constructor and destructor, which is typically a lot easier than trying to duplicate state.

This topic is closed to new replies.

Advertisement