A very common case: trying to call a pure virtual method from the constructor...
Constructors
struct Interface
{
Interface();
virtual void logInit() const = 0;
};
struct Concrete: Interface()
{
virtual void logInit() const { std::cout << "Concrete" << std::endl; }
};
Now, suppose the following implementation of Interface()
Interface::Interface() {}
Then everything is fine:
Concrete myConcrete;
myConcrete.pure(); // outputs "Concrete"
It's such a pain to call pure after the constructor, it would be better to factorize the code right ?
Interface::Interface() { this->logInit(); } // DON'T DO THAT, REALLY ;)
Then we can do it in one line!!
Concrete myConcrete; // CRASHES VIOLENTLY
Why ?
Because the object's build bottom up. Let's look at it.
Instructions to build a Concrete
class (roughly)
Allocate enough memory (of course), and enough memory for the _vtable too (1 function pointer per virtual function, usually in the order they are declared, starting from the leftmost base)
Call Concrete
constructor (the code you don't see)
a> Call Interface
constructor, which initialize the _vtable with its pointers
b> Call Interface
constructor's body (you wrote that)
c> Override the pointers in the _vtable for those methods Concrete override
d> Call Concrete
constructor's body (you wrote that)
So what's the problem ? Well, look at b>
and c>
order ;)
When you call a virtual
method from within a constructor, it doesn't do what you're hoping for. It does go to the _vtable to lookup the pointer, but the _vtable
is not fully initialized yet. So, for all that matters, the effect of:
D() { this->call(); }
is in fact:
D() { this->D::call(); }
When calling a virtual method from within a Constructor, you don't the full dynamic type of the object being built, you have the static type of the current Constructor invoked.
In my Interface
/ Concrete
example, it means Interface
type, and the method is virtual pure, so the _vtable does not hold a real pointer (0x0 or 0x01 for example, if your compiler is friendly enough to setup debug values to help you there).
Destructors
Coincidently, let's examine the Destructor case ;)
struct Interface { ~Interface(); virtual void logClose() const = 0; }
Interface::~Interface() { this->logClose(); }
struct Concrete { ~Concrete(); virtual void logClose() const; char* m_data; }
Concrete::~Concrete() { delete[] m_data; } // It's all about being clean
void Concrete::logClose()
{
std::cout << "Concrete refering to " << m_data << std::endl;
}
So what happens at destruction ? Well the _vtable works nicely, and the real runtime type is invoked... what it means here however is undefined behavior, because who knows what happened to m_data
after it's been deleted and before Interface
destructor was invoked ? I don't ;)
Conclusion
Never ever call virtual methods from within constructors or destructors.
If it's not that, you're left with a memory corruption, tough luck ;)