在 Visual C++ 中与虚拟继承层次结构一起使用时放置新崩溃

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

我在 C++ 中使用虚拟继承和 classes 的选择。它目前正在崩溃崩溃。它似乎在在线编译器中编译得很好,但是当我在 Visual Studio 中 运行 时,它崩溃了。

我有一个纯虚拟基础 class,它实际上被它的实现所继承。然后我有第三个 class 定期从实现中继承。我正在使用一个内部系统来创建和释放内存。在引擎盖下,它使用了一个新的布局和一个对齐的 malloc。然后它使用 free 来释放内存。我已经创建了这个最小示例。这不完全是我在做什么,但我似乎遇到了类似的问题。

#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!");
}

就像我说的,这个例子将在在线编译器中打印"Done"。然而,在 Visual Studio 2015 年,它似乎在没有蝙蝠对象的情况下崩溃了。我对虚拟继承和安置很陌生。有人看到问题了吗?

mallocreturns一些新的内存地址,operator new在该地址放置一个Bat,然后转换为Animal*调整 地址。现在 bat 变量指向 malloc 块内的某处。 free这是不可能的。

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

gcc 打印两个相同的地址,而 VC++ 打印两个不同的地址。即使不涉及多重继承,这两种行为都是完全正常的并且是标准允许的。大多数编译器实际上不会用单继承调整地址,但也有一些例外。

为了安全起见,不要指望两个地址相同。

可以通过动态转换为 void*:

来恢复原始地址
  free(dynamic_cast<void*>(bat));

应该没问题。当然,像往常一样,动态转换需要一个虚函数才能工作。

更新:dynamic_cast<void*>恢复初始指针,free仍然因VC++而崩溃。我不知道为什么。

在 C++ 程序中集成第三方内存管理器的正确方法是重载 operator newoperator delete

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

将它们放在您程序的任何一个 C++ 文件中(如果您有 DLL,则在所有 DLL 中)并正常使用 C++,没有定义错误的指针技巧。

有关详细信息,http://en.cppreference.com/w/cpp/memory/new/operator_new .