Placement new crashing when used with virtual inheritance hierarchy in Visual C++

628 views Asked by At

I am using virtual inheritance with a selection of classes in c++. It is currently crashing on destruction. It seems to compile fine in the online compilers, however when I run in Visual Studio, it crashes.

I have a pure virtual base class, which is being inherited virtually by its implementation. I then have a third class that is inheriting from the implementation regularly. I am using an internal system for creating and releasing memory. Under the hood it is using a placement new with a aligned malloc. It is then using free to free the memory. I have created this minimum example. It is not exactly what I am doing but I seem to get a similar problem.

#include <iostream>
#include <string>

int main()
{
    class Animal {
      public:
        Animal() { }
        virtual ~Animal() { }
        virtual void eat() { }

    };

    class Mammal : public virtual Animal {
      public:
        virtual void breathe() { }

    };

    class WingedAnimal : public virtual Animal {
      public:
        virtual void flap() { }
    };

    // A bat is a winged mammal
    class Bat : public Mammal, public WingedAnimal {

    };

   Animal* bat = new(malloc(sizeof(Bat))) Bat;
   bat->~Animal();
   free(bat);
   printf("Done!");
}

Like I said, this example will print "Done" in the online compiler. However in Visual Studio 2015 it seems to crash on free of the bat object. I am fairly new to virtual inheritance and placement new. Does anyone see the problem?

1

There are 1 answers

8
n. m. could be an AI On BEST ANSWER

malloc returns some fresh memory address, operator new places a Bat at that address, and conversion to Animal* adjusts the address. Now the bat variable points somewhere inside the malloc block. freeing it is impossible.

Bat* bat0 = new(malloc(sizeof(Bat))) Bat;
Animal* bat = bat0;
std::cout << bat0 << " " << bat << "\n";

gcc prints two identical addresses, while VC++ prints two different ones. Either behaviour is perfectly normal and allowed by the standard, even when no multiple inheritance is involved. Most compilers don't actually adjust the address with single inheritance, but there are some exceptions.

To be on the safe side, don't rely on the two addresses being the same.

It is possible to recover the original address by dynamic casting to void*:

  free(dynamic_cast<void*>(bat));

should be OK. Of course a virtual function is required for the dynamic cast to work, as usual.

Update: dynamic_cast<void*> recovers the initial pointer, but free still crashes with VC++. I have no idea why.

The right method to integrate a third party memory manager in your C++ program is to overload operator new and operator delete

void* ::operator new(size_t sz) { return my_managed_malloc(sz); }
void ::operator delete (void* ptr) { return my_managed_free(ptr); }

Place these in any one C++ file in your program (if you have DLLs, then in all DLLs) and use C++ normally, without ill-defined pointer tricks.

For more info, http://en.cppreference.com/w/cpp/memory/new/operator_new .