在 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 年,它似乎在没有蝙蝠对象的情况下崩溃了。我对虚拟继承和安置很陌生。有人看到问题了吗?
malloc
returns一些新的内存地址,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 new
和 operator 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 .
我在 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 年,它似乎在没有蝙蝠对象的情况下崩溃了。我对虚拟继承和安置很陌生。有人看到问题了吗?
malloc
returns一些新的内存地址,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 new
和 operator 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 .