1

Virtual function from official explanation is:

A virtual function is a member function that you expect to be redefined in derived classes. When you refer to a derived class object using a pointer or a reference to the base class, you can call a virtual function for that object and execute the derived class's version of the function.


Please see the code first:

#include<iostream>
using namespace std;

class A
{
public:
    A(){cout << "A()" << endl;}
    ~A(){cout << "~A()" << endl;}
};

class B:public A
{
public:
    B(): A(){cout << "B()" << endl;}
    ~B(){cout << "~B()" << endl;}
};

int main()
{
    A * pt = new B;
    delete pt;
}

Output is:

A()
B()
~A()

My question is:

  1. Destructor of base class can't be inherited by derived class, so why we make destructor of base class to be virtual?
  2. For the code above, I know this will lead to problem(here destructor of class B not called). I have searched so many articles or questions from google and stackoverflow and all of them tell me that destructor of base should be virtual but how "virtual" on destructor works? I mean that what the difference in core code level with/without "virtual" to the destructor?
curiousguy
  • 8,038
  • 2
  • 40
  • 58
MA Shengjing
  • 113
  • 1
  • 7
  • *"Destructor of base class can't be inherited by derived class"* - Incidentally, why do you think so? – IInspectable Aug 03 '17 at 02:18
  • If your derived class destructor is virtual then objects will be destrcuted in a order(firstly derived object then base ). If your derived class destructor is NOT virtual then only base class object will get deleted – Hariom Singh Aug 03 '17 at 02:19
  • Are you familiar with vtables? https://stackoverflow.com/a/99341/951890 – Vaughn Cato Aug 03 '17 at 02:26
  • @Hariom Singh, what you say is telling us the phenomenon but not explain why. That's the reason I post my question. – MA Shengjing Aug 03 '17 at 02:28
  • 1
    In this context, `virtual` in destructor has nothing different from `virtual` function: It just make sure the destructor of the actual object instance is invoked. What confuses you here? – Adrian Shum Aug 03 '17 at 02:35
  • @IInspectable, [see this article](https://stackoverflow.com/questions/14184341/c-constructor-destructor-inheritance) – MA Shengjing Aug 03 '17 at 02:35
  • @Adrian Shum, what confuses me is I think destructor is different from member function. As we can't override ~A() in class B if ~A() is virtual. – MA Shengjing Aug 03 '17 at 02:45
  • @MAShengjing I understand how it confuses you then. Conceptually you may just treat `~ClassName()` is just a special name to define a "destructor method". C++ could have chosen the name for "desctructor method" as `desctructor()` (for which you shouldn't have confusion why `vritual` is having effect here), although they have chosen to name it `~ClassName()`. – Adrian Shum Aug 03 '17 at 02:50
  • Use quote formatting for text that is quoted, and not when it isn't. Please cite source when quoting. – user207421 Aug 03 '17 at 02:58
  • Destructors may be seen as special in sense that constructors in derived classed do not "override" those in base class but "chain" to them (virtual or not). – el.pescado - нет войне Aug 03 '17 at 05:50

4 Answers4

2

delete pt; causes undefined behaviour if the destructor of A is not virtual.

The reason to make A's destructor virtual is to enable the use of delete pt; to delete a B object.

The rationale for this is that when the compiler sees delete pt;, in general, it has no way of knowing whether pt points to a B object or not, since that decision could have not been made until run-time. So you need to look up some run-time property of the object (in this case a vtable) to find out the right destructor to call.

Some other comments/answers suggest that the defined behaviour of your original code is to not call B's destructor , or something. However that is wrong. You are just seeing symptoms of undefined behaviour, which could be that or anything else.

M.M
  • 138,810
  • 21
  • 208
  • 365
1

If the destructor is marked virtual then when you call delete the destructor of the dynamic type of the object you have allocated will be called. In your example the static type of the object on the heap is A, whereas the dynamic type is B.

Since you have not marked the destructor virtual, there will be no run time dispatch and the destructor for A is called. This is wrong and should be fixed. If you are planning on using a class in a polymorphic way, make sure it's destructor is virtual, so that instances of the derived classes can release any resources they have acquired.

Curious
  • 20,870
  • 8
  • 61
  • 146
1

It can help to imagine how vtables are implemented.

A class with a virtual method has a pointer to a table of function pointers as its first element.

virtual on a method means there is an entry in the virtual function table for it.

For a method, inherited classes replace the entry when they override.

For destructors the entry is actually "how to call delete on this object". All descended classes automatically override it. It turns a call of delete base_ptr into if (base_ptr) base_ptr->vtable->deleter(base_ptr); conceptually.

Then the deleter of derived is effectively (almost) delete static_cast<derived*>(ptr); That does what a usual call to delete does, it calls the destructors in order.

Failing to do that leaves you with undefined behaviour. Often the UB is that the base class dtor is called.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
0

If the function is not virtual, the C++ runtime will directly call the mangled function. For example, in the above code, the destructor may be mangled as _ZNK3AXXXXXXXXX (fake name). So when you call delete pt, the runtime will execute _ZNK3AXXXXXXXXX.

However, the result is different if the function is virtual. Like @Yakk said, a class with virtual functions will have a vtable, whose entries are function pointers. It may be at the top of the address space of this class, or at the bottom, depending on the implementation of class model. Any call of virtual function will look up this table. If the dtors are virtual, the function in derived class will override the corresponding entry in the table. When you call it using a pointer or reference, the C++ runtime will call the function in the table. Look at your example again. The entries of object pointed by pt have been overrode by class B, so when you call delete pt, the overrode version of dtor will be invoke. That's is the difference.

  • *"the C++ runtime will call the function in the table"* - That's an implementation detail, and not part of the language specification. And in the majority of cases, it's the compiler that generates an indirect call through the v-table. Regardless, any implementation can choose to implement virtual dispatch any way it likes. – IInspectable Aug 03 '17 at 10:19
  • The vtable approach happens to be simple, clean and fast, depends only a local information (no need to do a global analysis), it is O(1)... it has so many nice properties, in practice, every implementation that is not an interpreter is going to use it. – curiousguy Aug 20 '17 at 18:36