问题
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:
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. free
ing 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 .
来源:https://stackoverflow.com/questions/41246633/placement-new-crashing-when-used-with-virtual-inheritance-hierarchy-in-visual-c