As you know, if you want to delete through a pointer to the parent class, the class must have a virtual destructor in order for the delete operator to call the correct destructor of the child class.

I am also interested in the case when the child class does not add anything to the parent that should be destroyed. For example, it does not add any fields to the base class at all. Is it possible in this case to ignore the destructor's virtuality rule, or even in this case, the deletion will be UB?

Example code: https://ideone.com/B3Wx7m

 struct A { int *p; A(unsigned n) : p(new int[n]) {} ~A() { delete [] p; } }; struct B : A { B() : A(4) {} }; int main() { A *a = new B(); delete a; return 0; } 
  • one
    Attempting to legalize UB? Hehe - αλεχολυτ

1 answer 1

The standard clearly states that such deletion causes undefined behavior. No reservations are made on whether any fields are added to the heir class or not.

5.3.5 Delete
It has been noted that the have a virtual destructor or the behavior is undefined. [...]

On the destructor of the class, some implicit utility functions are also usually superimposed (may overlap), which can work incorrectly when the wrong destructor is called.

In particular, in a typical implementation, the destructor is charged with calling the correct function operator delete , which can be overloaded for each class separately. That is, if the static function operator delete were defined in struct B , then without a virtual destructor, it would have to be provided with some other methods to provide its functionality. (The authors of the standard, most likely, were guided by just such an implementation of a call to an overloaded operator delete .)

for example

 #include <cstdlib> struct A { // virtual ~A() {} }; struct B : A { static void *operator new(size_t s) { return (char *) std::malloc(s * 2) + s; } static void operator delete(void *p, size_t s) { std::free((char *) p - s); } }; int main() { A* p = new B; delete p; } 

In popular implementations, such code will fall due to an invalid operator delete called. But it is enough to add a virtual destructor to A , and everything will be fine. Note that both members of B are static functions, that is, nothing is physically added to B

  • Well, in the question it was assumed that I am not going to overload delete either ... By the way, you have some strange new and delete options - I didn’t have a size parameter in another question ... - Qwertiy
  • @Qwertiy: The operator delete variant, in which there is a parameter with a size, is supported starting with C ++ 14. And about "supposed-not supposed" ... Hidden service functions performed by the destructor are not stipulated in the language. This is nothing more than an example of one of them. There may be others who will suffer from non-virtualness. - AnT
  • @Qwertiy, why add extra restrictions to the standard when they are not needed (I mean the topic of the question)? It is easier to add one prohibition than to support each case that may occur. - ixSci
  • @AnT Destructor is not assigned the correctness of calling any functions. Overloaded functions and methods are defined at the broadcast stage and destructors including, the destructor itself is a special method called at runtime and, accordingly, there is nothing defining there. You gave a quote and added a code that is not appropriate to it, there’s nothing to do with overloaded operators, since without a virtual destructor there is definitely a wrong deletion and UB. Although if you remove speculation, this is the answer. - Andrey Sv
  • @Andrey Sv: Did you even read the question and my answer? First, the functionality described by me is presented here in order to show the author that the functionality of the virtual destructor does not lose its value even in the case when the heir class did not add any new fields. (This is an important part of the question.) Secondly, my answer contains only hard facts - this is how the existing C ++ implementations implement the functionality of the overloaded operator delete . Moreover, the standard of the language, although it does not prescribe just such an approach to implementation, it nevertheless is tailored to it. - AnT