Static and dynamic destructor of derived class.
Usage
- Deletes a derived class instance by base class pointer
Explain
By using LSP, a base class pointer can handle its any child class' instances. However, it does not work as what we expect. Code 1 shows what problem is.
// 01_C_NormalDestructor.cpp
#include <iostream>
using namespace std;
class BaseClass
{
public:
~BaseClass() {};
};
class DerivedClass : public BaseClass
{
public:
DerivedClass()
{
cout << "Resource allocated" << endl;
}
~DerivedClass()
{
cout << "Resource released" << endl;
}
};
int main()
{
BaseClass* p = new DerivedClass;
delete p;
}
// Compile: clang++ -std=c++14
// -o 01_C_NormalDestructor 01_C_NormalDestructor.cpp
Resource allocated
As a result, DerivedClass instance is not deleted in Code 1. Because destructor of BaseClass is not inherited in DerivedClass, compiler does not call destructor of DerivedClass for delete. Only, compiler calls destructor of the type of the pointer.
To delete DerivedClass instance, there are two ways, static or dynamic.
The first way is static, which uses static_cast. Code 2 shows how it works.
// 02_C_StaticDestructor.cpp
#include <iostream>
using namespace std;
class BaseClass
{};
class DerivedClass : public BaseClass
{
public:
DerivedClass()
{
cout << "Resource allocated" << endl;
}
~DerivedClass()
{
cout << "Resource released" << endl;
}
};
int main()
{
BaseClass* p = new DerivedClass;
delete static_cast<DerivedClass*>(p);
}
// Compile: clang++ -std=c++14
// -o 02_C_StaticDestructor 02_C_StaticDestructor.cpp
Resource allocated
Resource released
Instead of using the pointer, we cast it to DerivedClass with static_cast. By using static_cast, p is changed to the pointer of DerivedClass in compile time, and delete will destruct DerivedClass instance. Because casting is done in compile time, it is faster than next. However, developer takes all responsibility for this casting.
The second way is to add Virtual Destructor to BaseClass. Code 3 is about Virtual Destructor.
// 03_C_VirtualDestructor.cpp
#include <iostream>
using namespace std;
class BaseClass
{
public:
virtual ~BaseClass() {}
};
class DerivedClass : public BaseClass
{
public:
DerivedClass()
{
cout << "Resource allocated" << endl;
}
~DerivedClass()
{
cout << "Resource released" << endl;
}
};
int main()
{
BaseClass* p = new DerivedClass;
delete p;
}
// Compile: clang++ -std=c++14
// -o 03_C_VirtualDestructor 03_C_VirtualDestructorvv.cpp
Resource allocated
Resource released
By using Virtual Destructor, delete can find the destructor of DerivedClass in run time. It is slower than the static way, because it takes time to find DerivedClass in run time. Also, we call it dynamic binding.
The default strategy of C++ compiler is static binding, which means compiler decides as much as it can in compile time. So, in compile time, compiler can optimize its output. However, dynamic binding is done in run time, so compiler cannot optimize it and needs to make some something helpful to find target instance and bind the target. It takes more cpu power compared to static binding. However, dynamic binding gives developer freedom to adapt runtime circumstance.
Summary
- If an application is based on LSP, be careful to delete an instance.
- Casting and Virtual Desctuctor is solutions for deleting derived class instance.
- Static binding is faster, but dynamic binding is changeable.
COMMENTS