大家晚上好。
一段代码片段胜过千言万语:
// Storage suitable for any of the listed instances
alignas(MaxAlign<Base, Derived1, Derived2>::value)
char storage[MaxSize<Base, Derived1, Derived2>::value];
// Instanciate one of the derived classes using placement new
new (storage) Derived2(3.14);
// Later...
// Recover a pointer to the base class
Base &ref = *reinterpret_cast<Base*> (storage);
// Use its (virtual) functions
ref.print();
// Destroy it when we're done.
ref.~Base();
正如您所看到的,我只想通过其基类访问实例,而不实际存储基类指针。请注意,在第二部分中,Derived2
类型信息将丢失,因此我只剩下storage
并保证其中包含一个派生实例。
由于placement new永远不会调整目标指针,因此归结为使用reinterpret_cast
向上转换为基类。现在我知道这很危险,因为在某些情况下,更合适的static_cast
会调整指针。 [1]
事实上,它会触发未定义的行为。你会找到完整的代码here on Coliru(g ++ 4.9.0),它会在运行时迅速崩溃。同时在我的电脑上(g ++ 4.8.2),一切都很好。请注意,在g ++ 4.9上,在调用函数之前输出两个指针显示相同的值......并且有效。
所以我试图向后解决问题:轻推派生实例,使指向 Base
的指针等于storage
。
void *ptr = static_cast<Derived2*>(reinterpret_cast<Base*>(storage));
new (ptr) Derived2(3.14);
没有运气。这个东西在g ++ 4.8上仍然运行良好,并且在g ++ 4.9上岌岌可危。
编辑:想到它,上面并不那么聪明。因为,如果派生实例在 storage
之前结束,那该怎么办...
所以我的问题是:我正在尝试实现的解决方案吗?或者,[1]中提到的案例是否足够明确,我可以编写可以使用类子集的代码(比如,没有虚拟继承)?
答案 0 :(得分:3)
我刚刚修改了你的代码,似乎reinterpret_cast不是问题所在。 可以复制此错误的最小代码就是这样
Coliru链接:http://coliru.stacked-crooked.com/a/dd9a633511a3d08d
#include <iostream>
struct Base {
virtual void print() {}
};
int main(int, char**) {
Base storage[1];
storage[0].print();
std::cout <<"Succeed";
return 0;
}
触发此错误的充分条件是
&#34;存储&#34;变量在堆栈上作为数组分配
print()必须是虚方法
编译器选项应为-O2
如果使用-O1,程序将编译并运行没有问题。
此外,这个错误似乎只在用g ++ 4.9.0编译时出现。 如果使用VS2012 / 2013或g ++ 4.7.2编译它可以正常运行(您可以在http://www.compileonline.com/compile_cpp_online.php上进行测试)
从上面的判断来看,我认为这可能是编译器特定的问题。
注意:强> 本答案中给出的程序的实际输出与OP不同。它不显示分段错误。但是,当成功运行时,它应该打印&#34;成功&#34;,当它在Coliru上运行时不会显示。
修改强> 修改了代码以复制错误。不需要派生类。