You are here:

C++/Virtual destructors

Advertisement


Question
QUESTION: The following code gives the error
 *** glibc detected *** free(): invalid pointer: 0x0804a00c ***
 Aborted
when run after compiling with gcc 4.6.2.  Could you say why?

//----------------------------------------
#include <vector>

class A {
 int a;
public:
 ~A(){}
};

class B : public A{
public:
 virtual ~B() {}
};

int main(){
 A* x = new B;
 delete x;
}
//--------------------

Thanks.

ANSWER: Well, yes, let's see if I can explain it for you...

By making B::~B virtual you allow B objects and sub-classes of B objects to be destroyed through pointers to B.

However, this is not the case for As - if you delete a B via a pointer to A then A::~A is NOT virtual and so does not invoke B::~B, and thus will be destroyed as an A and not a B.

This is OK - the free store (a.k.a. heap) knows the size of the block and can delete it - if it were not for that pesky virtual specifier on the destructor of B.

To see why let's modify your main so we can see what is going on:

   int main()
   {
     B * xb = new B;
     A* x = xb;
     std::cout <<    "new B as B* = " << xb
         << "\nnew B as A* = " << x
         << "\n"
         ;
     delete x;
   }

Here I initially store the B* returned from new B in a B* named xb, then I assign it to the A* x.

Next I have added some output so we can observe the values of the pointer to the object when held as the created B*, and also as the A*.

Do not forget to add in #include <iostream> (in fact as header vector is not used iostream can replace it!).

Now if we modify class B so that B::~B is not virtual:

   class B : public A
   {
   public:
      ~B() {}
   };

And we build and run your program (I am using an x64 build from Microsoft C++ 2010) we get output something like so:

   new B as B* = 0000000000487930
   new B as A* = 0000000000487930


Both the B* and A* 'views' of the B object have the same address and there is no crash when trying to delete the B through the x A* pointer.

We can even add state to B class instances, e.g.:

   class B : public A
   {
     int b;

   public:
      ~B() {}
   };

And we get similar results when the program is rebuilt and run:

   new B as B* = 0000000000537930
   new B as A* = 0000000000537930

Now if we revert to your original definition for B:

   class B : public A
   {
   public:
      virtual ~B() {}
   };

Rebuild and run the program we see something a bit odd:

   new B as B* = 0000000000697930
   new B as A* = 0000000000697938

The B* and A* 'views' of where the object is are now different. The B* pointer indicates that the whole B object starts at 0x697930 but the A* sub-object part of the B object now starts at 0x697938, 8 bytes (or 64-bits) beyond the start of the whole object. Remember I am getting these figures from a 64 bit build so pointers are 64 bits in size.

What is going on?

Well if an object does not have any virtual member functions, like objects of class A, then it has no need for support for such facilities - such support adds complexity in space and time.

If a class is derived from a single class having no virtual member function support and it also has no virtual member function support - as is the case of my two modified versions of your B class:

   class B : public A
   {
   public:
      ~B() {}
   };

and:

   class B : public A
   {
     int b;

   public:
      ~B() {}
   };

Then the layout in memory of the derived class' objects simply extend the base class. In this example such a B object would look something like so (might look a bit better viewed using a fixed pitch font like Courier):

   B*  ->  a          A subobject start - starts at 0 offset from start of B
   A*  --/^
         b          Extended B state follows on from A part

However, in the case of your original example B has a virtual destructor and therefore objects of B require virtual member function despatch support. This is usually (in practice always) in the form of a table of function pointers for the current override set of virtual member functions. Such tables are common for all instances (objects) of such classes, but each object requires a pointer to the table - i.e. such support requires additional state for each object of classes that require it. The table is commonly known as a vtable, and the pointer is commonly the first piece of state in objects that require a vtable pointer. This modifies the layout of B objects, particularly as to where the base A subobject starts within the whole B object:

   B*  ->  vtable pointer
   A*  ->  a          A subobject start - starts at NON ZERO offset from start of B
         b          Extended B state follows on from A part

So in this case the A subobject is offset from the start of the B object it is part of by at least the size of the vtable pointer.

Much of the low level details and managing of subobject pointer offsets within objects is handled by the compiler. But in this case you managed to skirt around it by not having virtual function despatch support in A objects hence it did not know that it had to adjust A* to be a B*. In general the compiler cannot do this as it cannot always know that the A* it is passed points to a B or an A or some other derived subtype from A.

When you delete the B object through the A* x the unadjusted value pointing to the A part of the B is passed to delete and this is used to try to delete a block of memory _starting_ at this (offset) address - and the free store complains as it has no block allocated at this address.

If you intend a class to be used as a base class then give that class a virtual destructor. This you did not do for class A. If you did then the layout of B would look something like so:

   B*  ->  vtable pointer      A subobject start - starts at 0 offset from start of B
   A*  --/^
         a          Explicit A state
         b          Extended B state follows on from A part

The above corresponds to specifications for classes A and B like so:

   class A
   {
     int a;
   public:
     virtual ~A(){}        // A destructor virtual
   };

   class B : public A
   {
     int b;
   public:
      ~B() {}          // B destructor inherits virtual property from A
   };

Rebuilding and running the modified program should yield results similar to:

   new B as B* = 0000000000177930
   new B as A* = 0000000000177930

That is, the A subobject part of B starts at the same point as the B object as a whole.

By the way these are a simple cases of object layouts. If you want to really see some tricky object with subobject layouts consider complex objects formed from class hierarchies using multiple inheritance and virtual base classes! You might like to look at layout choices picked by different compilers (g++ vs. MSVC++ for example).

Hope this helps.



---------- FOLLOW-UP ----------

QUESTION: It seems that the compiler should flag an error for my program.  Whenever an assignment A*a = new B; gets made, there is information to check if B has a vtable and A does not?  Is this correct?  In any case, the compiler is checking if B is a subtype of A, right?  I am not considering explicit typecasting here -- when I do that I am taking everything into my hands, of course.

Thanks.

Answer
Additional 2:
-------------

Ooops - got a bit bogged down with previous additional text and forgot to mention that if you really want to check your code for these sorts of problems (and many, many more - probably too many) then you should consider using static analysis tools such as lint (original, still going, very good but the Gimpel versions are very much not free unfortunately). Most other such tools also tend to be paid for software - also usually for quite a lot of cash :(

Alas the free tools I have come across are not much use, e.g. splint only supports C code and cppcheck seems not to check for this specific problem, at least not out of the box :(.


Additional:
-----------

Sorry you did not get the same results from g++ as I did. It may be the version of the GNU compiler you are using. You should have taken away that warning against this sort of thing is very much a quality of implementation issue and is very compiler specific.

For your reference I tested using g++ version 4.6.3 under Ubuntu Linux 12.04 for x64 and got these results:

   $ cat main.cpp

   class A
   {
       int a;
   public:
       A() : a(0) {}
       ~A() {}
   };

   class B : public A
   {
   public:
       virtual ~B() {}
   };

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

   $ g++ -Wall -pedantic -Wextra -Weffc++ -o main main.cpp
   main.cpp:10:7: warning: base class ‘class A’ has a non-virtual destructor [-Weffc++]
   $ ./main
   *** glibc detected *** ./main: free(): invalid pointer: 0x0000000000cbc018 ***

The above was copied off a terminal session and shows:

- The code in the source file I am building (cat main.cpp) - you will note that this is pretty much your problem code except that I added a public default constructor to A to suppress other warnings.

- The g++ command used to build the program:
   g++ -Wall -pedantic -Wextra -Weffc++ -o main main.cpp

- The output from the build:
   main.cpp:10:7: warning: base class ‘class A’ has a non-virtual destructor [-Weffc++]

- The first line of the crash report on program execution:
   *** glibc detected *** ./main: free(): invalid pointer: 0x0000000000cbc018 ***

As far as I can tell from your description the alternate situation you mention has the same problem. If I understand correctly all you did was swap the 'special' destructor member functions with f. This leaves us with no virtual despatch required for A (A::f not virtual) but is for B (B::f is virtual) which is same case as using non-virtual A::~A and virtual B::~B so will give the same effect on the object layout: the pointer to the A subobject will be below the vtable pointer required for B objects in the whole B object.


-- End additional text
------------------------------------------------------------------------------------------------------


Indeed it is possible for such usages to be flagged - at least as a compiler specific warning - I think making them an error would be a bit too much however (although allowable - and note that compilers usually have options to treat warnings as errors - e.g. GNU compiler -Werror etc.).

So the next question is: have you turned on the necessary compiler warning options?

For g++ as well as the usual -Wall -pedantic -Wextra there are also a set of C++ specific warning options including -Wnon-virtual-dtor and -Weffc++. The first of these will warn if a class has virtual functions and an accessible but non-virtual destructor (which will not catch the case here) and the second warns if some of the guidelines in Scott Meyer's Effective C++ are violated, including Item 14 : Make destructors virtual in base classes (and turns on -Wnon-virtual-dtor) and will catch the case here. See:

   man g++

for more details.
(I found dumping the long man page text into a file:

   man g++ > g++-options.txt    

and using an editor to view and search the full text easier than paging through the text one screenful at a time)

However, compilers are not required to diagnose this situation by the ISO C++ standard wording. This probably comes from the fact that what happens when you do this sort of thing depends quite a lot on how the compiler and the plaform environment the program runs on function, and C++ works on all sorts of systems. It depends on things such as the particular chosen object layout and virtual despatch mechansism (no matter that using a vtable and having an initial pointer to one if required in an object's layout are pretty much _the_ ways used by many/most C++ compilers!!) and the way the free store works (you could imagine a C++ layout friendly free store implementation that knows about sub-object offsets within allocated blocks).

Hence all that ISO C++ standard states is that what you are doing is undefined behaviour:
(from the latest standard document section 5.3.5 Delete, parargraph 3)

   "(delete object), if the static type of the object to be deleted is different from its
    dynamic type, the static type shall be a base class of the dynamic type of the object
    to be deleted and the static type shall have a virtual destructor or the behavior is
    undefined."

The static type of the object when looking just at the expression used with delete, being just x in this case and x being of type A*, would yield a static type for the object of A.
The dynamic type is the type of the most derived object, in this case B.

Note that the requirements are that A must be a base of B (OK so far) AND "the static type shall have a virtual destructor or the behavior is undefined". Which in this case translates to: A shall have a virtual destructor or the behavior is undefined. Undefined behaviour is defined thus:

   "behavior for which this International Standard imposes no requirements
    [ Note: Undefined behavior may be expected when this International Standard omits any
      explicit definition of behavior or when a program uses an erroneous construct or
      erroneous data. Permissible undefined behavior ranges from ignoring the situation
      completely with unpredictable results, to behaving during translation or program
      execution in a documented manner characteristic of the environment (with or without
      the issuance of a diagnostic message), to terminating a translation or execution
      (with the issuance of a diagnostic message). Many erroneous program constructs do not
      engender undefined behavior; they are required to be diagnosed.
   -- end note ]"

Hope this helps.  

C++

All Answers


Answers by Expert:


Ask Experts

Volunteer


Ralph McArdell

Expertise

I am a software developer with more than 15 years C++ experience and over 25 years experience developing a wide variety of applications for Windows NT/2000/XP, UNIX, Linux and other platforms. I can help with basic to advanced C++, C (although I do not write just-C much if at all these days so maybe ask in the C section about purely C matters), software development and many platform specific and system development problems.

Experience

My career started in the mid 1980s working as a batch process operator for the now defunct Inner London Education Authority, working on Prime mini computers. I then moved into the role of Programmer / Analyst, also on the Primes, then into technical support and finally into the micro computing section, using a variety of 16 and 8 bit machines. Following the demise of the ILEA I worked for a small company, now gone, called Hodos. I worked on a part task train simulator using C and the Intel DVI (Digital Video Interactive) - the hardware based predecessor to Indeo. Other projects included a CGI based train simulator (different goals to the first), and various other projects in C and Visual Basic (er, version 1 that is). When Hodos went into receivership I went freelance and finally managed to start working in C++. I initially had contracts working on train simulators (surprise) and multimedia - I worked on many of the Dorling Kindersley CD-ROM titles and wrote the screensaver games for the Wallace and Gromit Cracking Animator CD. My more recent contracts have been more traditionally IT based, working predominately in C++ on MS Windows NT, 2000. XP, Linux and UN*X. These projects have had wide ranging additional skill sets including system analysis and design, databases and SQL in various guises, C#, client server and remoting, cross porting applications between platforms and various client development processes. I have an interest in the development of the C++ core language and libraries and try to keep up with at least some of the papers on the ISO C++ Standard Committee site at http://www.open-std.org/jtc1/sc22/wg21/.

Education/Credentials

©2016 About.com. All rights reserved.