我在问以下方法是否(以及为什么)a)合法和b)道德。我要强调的是C ++ 03,但也欢迎关于C ++ 11的注释。我们的想法是防止派生类本身可以默认构造为实现愚蠢的B::B(int foo) : A(foo) {}
构造函数。
class Base {
private:
int i;
Base(int i) : i(i) {}
protected:
Base() {}
public:
static Base* create(int i);
};
class Derived : public Base {
};
Base* Base::create(int i) {
Derived* d = new Derived();
Base* b = static_cast<Base*>(d);
delete b;
new(b) Base(i);
return d;
}
我的直觉告诉我,这里有些东西可疑。如果任何Derived
类在其构造函数中访问Base
成员,我想要在其他地方,但否则我很难看到为什么这种方法不好的正确理由。
无论如何,如果您认为这是一种可接受的方法,那么如何处理参考成员(如int& Base::j
)?
注意:这是How can I fake constructor inheritance in C++03?的后续问题。
修改:发帖时我一定是分心了。当然,我不是delete b
而是b->~Base()
。我责备低血糖!
答案 0 :(得分:3)
代码不正确并触发未定义的行为。您的Base
类没有虚拟析构函数,这意味着delete b
将导致未定义的行为。
调用delete
时UB的原因在于它不会释放派生资源(这似乎是代码的目的,哎哟!),根据两个对象的布局,它将尝试释放分配的内存,这可能有效或无效。如果它无法释放内存,它可能会崩溃,如果它成功放置,则以下行中的新调用将尝试初始化已经释放的内存中的对象...
即使您将代码(试图避免重新分配问题)更改为:
Base* Base::create(int i) {
Derived *d = new Derived;
Base * b = static_cast<Base*>(d);
b->~Base(); // destruct, do not deallocate
new (b) Base(i);
return d;
}
如果没有delete
并因此未定义行为的特定源已消失,则代码仍然是未定义的行为(可能有太多方法甚至没有提及)。一旦对析构函数的调用仍然是UB,即使不是这样,重新创建Base
类型的事实意味着该对象的动态调度可能会认为该对象是Base
而不是Derived
而不是vtable
对象(在vptr
s的情况下,指向RunTime类型信息的Base
将引用Derived
,而不是{{1}})
可能还有两三种其他问题可能出错,我现在想不到......
答案 1 :(得分:1)
delete b
不只是调用Base
的析构函数,它还会释放new
返回的内存并调用Derived
的析构函数[假设您打算Base
有一个虚拟的析构函数...如果你打算将它作为非虚拟的,那么这个行为只是以未开始的方式定义]。这意味着您随后使用placement new会在内存中构建一个不再有效的全新Base
对象,并且您永远不会替换之前销毁的Derived
部分。简而言之,你所做的一切甚至都没有达到正确的行为。
坦率地说,我看不出你要做什么...为什么必须Derived
默认构建,而不是仅仅转发参数?它不是愚蠢的,它是事情的方式。