Middle+
What subtleties exist when calling virtual functions from a constructor or destructor?
sobes.tech AI
Answer from AI
When calling a virtual function from a constructor or destructor, polymorphism does not work as usual.
From the constructor:
- The implementation of the virtual function corresponding to the current constructor's type (i.e., the class whose constructor is executing) will be called. Implementations from derived classes will not be accessible because the objects of derived classes are not fully constructed yet.
From the destructor:
- Similarly, the implementation of the virtual function corresponding to the current destructor's type will be called. After the base class destructor executes, the part of the object corresponding to the derived class has already been destroyed.
Why is this?
- During the construction of a derived class object, its parts inherited from base classes are created sequentially. Until the derived class constructor finishes, the object is not a full instance of the derived class.
- During destruction, the process is reversed: first, the derived class destructor is called, then the base class destructors are called in the reverse order of construction. By the time the base class destructor is called, the part of the object corresponding to the derived class has already been destroyed.
Example:
#include <iostream>
class Base {
public:
Base() {
// Call virtual function from constructor
// Base::greet() will be called
greet();
}
virtual ~Base() {
// Call virtual function from destructor
// Base::greet() will be called
greet();
}
virtual void greet() {
std::cout << "Hello from Base" << std::endl;
}
};
class Derived : public Base {
public:
Derived() : Base() {}
~Derived() override {}
virtual void greet() override {
std::cout << "Hello from Derived" << std::endl;
}
};
int main() {
// When creating a Derived object, Base::Base()
// will call Base::greet()
// Then, Derived::Derived() constructor finishes
// When deleting the object, Derived::~Derived() is called,
// then Base::~Base() is called,
// which calls Base::greet()
Derived* obj = new Derived();
delete obj;
return 0;
}
The program output will be:
Hello from Base
Hello from Base
Instead of the expected polymorphic behavior (Hello from Derived).
Implications:
- Avoid calling virtual functions from constructors and destructors if polymorphic behavior based on the derived class type is expected.
- If actions dependent on the object type need to be performed during construction/destruction, consider alternative approaches, such as calling non-virtual functions or passing information through constructor parameters.
This is an important rule to ensure correct object behavior in C++, especially with inheritance.