Can someone explain this interaction to me? (C++)
Posted by Nicenamebtw@reddit | learnprogramming | View on Reddit | 5 comments
#include <iostream>
using namespace std;
class A
{
public:
A() { cout << "A constructor" << endl; }
~A() { cout << "A destructor;" << endl; }
virtual void print() const
{
cout << "A print." << endl;
}
};
class B : public A
{
public:
B() { cout << "B constructor" << endl; }
~B() { cout << "B destructor" << endl; }
void print() const override
{
cout << "B print" << endl;
}
};
int main()
{
A *a = new B;
a->print();
delete a;
}#include <iostream>
using namespace std;
class A
{
public:
A() { cout << "A constructor" << endl; }
~A() { cout << "A destructor" << endl; }
virtual void print() const
{
cout << "A print." << endl;
}
};
class B : public A
{
public:
B() { cout << "B constructor" << endl; }
~B() { cout << "B destructor" << endl; }
void print() const override
{
cout << "B print" << endl;
}
};
int main()
{
A *a = new B;
a->print();
delete a;
}
Output:
A constructor
B constructor
B print
A destructor
I understand why the object is created as an object of B class, since 'new' invokes the constructor of class B. But I don't understand why only the destructor for A is called and not for B. Please explain, I would love to understand the logic behind this.
mredding@reddit
Under the hood, the compiler generates a virtual table. Vtables are not explicitly required by the C++ spec, but it's how all the compilers do it. This is a static table of function pointers - each abstract class and derived class has it's own table. Your instance points to its table.
So then, if you have a
B
instance, but through type erasure you only have anA
pointer, then calling a method will undergo a lookup so the correct implementation is called.Maybe.
The vtable only contains entries for
virtual
methods. In your example, the only entry in the vtable of bothA
andB
will be forprint
. You didn't make~A
virtual, so when youdelete a
, the compiler doesn't knowa
is pointing to aB
. The compiler only knows the non-virtual interface presented to it through theA
type.If you google OOP in C, you can find tutorials for building objects in C, including modeling inheritance, and polymorphism. The object code generated by this exercise typically matches the C++ equivalent exactly. The first C++ compiler - CFront, was originally a transpiler to C.
So why not just use C? Because the two languages have different type systems. C++ grants you much more type safety - and type safety is more than just catching errors; compilers are theorem provers, and they use type information in order to optimize more aggressively. C and C++ are both high level abstract languages, they're not high level assembly languages. It's not about what machine code they generate.
ScholarNo5983@reddit
You need to make the A and B destructors virtual.
lurgi@reddit
To expand a little upon this, if you delete an object of a derived class via a pointer to the base class and the destructor is not virtual, only the base class destructor will be called. If you delete it via a pointer to the derived class then both destructors will be called.
Does that mean you should make destructors virtual? Maybe. If you know that you will never extend the class or you know that it will never be destroyed via a base class pointer, then maybe not. There is a cost to making that first function virtual and you might not want to pay it. If you have other virtual functions already then you probably should go ahead and make the destructor virtual.
ScholarNo5983@reddit
Just to add to this. If the plan is to only have one destructor and to not use virtual, then the destructor should also be made private:
That way the compiler will help to enforce that design pattern.
Total-Box-5169@reddit
This is why is good to have warning enabled: https://godbolt.org/z/Pncc3E1MK