Will Derived class type compatibility to base class cause a memory leak?
I understand that derived class is type compatible with a pointer to its base class. In the given sample code, new bar
object construction takes place calling foo::foo()
followed by bar::bar()
. In the respective constructors, I am allocating resources to class members foo::int *a
and bar::int *b
.
Now I am initializing thus constructed object to base class type. With obj
, I can call the base class destructor but not the derived class destructor. So, how can I deallocate the derived class resources in this case? Is this not a memory leak ?
#include <iostream>
class foo
{
int *a;
public:
foo()
{
a = new int[5];
std::cout << "\n foo constructor" << std::endl;
}
~foo()
{
std::cout << "\n foo destructor" << std::endl;
开发者_开发知识库 delete[] a;
}
};
class bar : public foo
{
int *b;
public:
bar()
{
b = new int[5];
std::cout << "\n bar constructor" << std::endl;
}
~bar()
{
std::cout << "\n bar destructor" << std::endl;
delete[] b;
}
};
int main()
{
foo *obj = new bar; // Derived class object is type compatible with base class
delete obj; // Equivalent to obj->~foo();
return 0;
}
Thanks.
This is where the idea of the "virtual destructor" comes in. Technically speaking, if you delete an object through a pointer of a base class type, you must mark that base class destructor virtual or the result is undefined. If you do mark the destructor virtual, the meaning is different from other virtual functions. Instead of meaning "derived classes override this behavior," it means "when deleting this object through a base class pointer, call the derived destructors before calling the base constructor." This is the behavior you want.
As a general rule, any class you define that you plan on subclassing should have a virtual destructor to prevent this sort of problem.
If you delete
a derived class object via a pointer to one of its base classes, the base class destructor must be declared virtual
, otherwise the behavior is undefined.
If ~foo()
is declared virtual
, you're good to go. ~bar()
will be called first, then ~foo()
.
It is actually an undefined behavior.
From Standard docs. 5.3.5.3 Delete,
In the first alternative (delete object), if the static type of the operand is different from its dynamic type, the static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual destructor or the behavior is undefined. ......
Do this,
virtual ~foo()
{
//your code
}
This ensures that doing delete *pFoo
also invokes derived classes destructor (~bar()
). The order of invocation would be ~bar()
followed by ~foo()
.
It will also be good if you do the same for ~bar()
also, that is,
virtual ~bar()
{
//your code
}
Although it's not that much necessary for this scenario if you don't want to further derive from bar
and want to use bar*
for it's derived classes.
精彩评论