各位晚上好。
一个代码片段将值一千字:
// 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]
事实上,它触发了未定义的行为。您可以在 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] 中提到的案例是否定义得足够好,以至于我可以编写适用于类子集的代码(例如,没有虚拟继承)?