1

我在问以下方法是否(以及为什么)是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)?

注意:这是如何在 C++03 中伪造构造函数继承的后续问题?.


编辑:发布问题时我一定分心了。当然,不是delete b我的意思b->~Base()。我怪低血糖!

4

2 回答 2

3

代码不正确并触发未定义的行为。您的Base类没有虚拟析构函数,这意味着delete b将导致未定义的行为。

UB 在调用中的原因delete范围从它不会释放派生资源(这似乎是代码的目的,哎呀!),到它会尝试释放分配的内存,这可能工作与否,取决于两个对象的布局。如果它未能释放内存,它可能会崩溃,如果它成功,则下一行中的放置 new 调用将尝试初始化内存中已经释放的对象......

即使您将代码(试图避免释放问题)更改为:

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类型的事实意味着该对象的动态调度可能会将对象视为 aBase而不是Derived对象(在vtables 的情况下,指向vptr运行时类型信息的将指代Base,而不是Derived

可能还有两三件其他事情可能出错,我现在想不起来......

于 2011-11-03T15:02:56.053 回答
1

delete b不只是调用Bases 析构函数,它还释放返回的内存new并调用Deriveds 析构函数[假设您打算Base拥有一个虚拟析构函数......如果您打算让它是非虚拟的,那么行为开始时只是未定义的]。Base这意味着您随后对placement new 的使用是在内存中构建一个不再有效的全新对象,并且您永远不会替换Derived您之前销毁的部分。简而言之,您所做的任何事情都没有接近正确的行为。

坦率地说,我看不到您要做什么...为什么必须Derived默认构造,而不仅仅是转发参数?它并不愚蠢,它是做事的方式。

于 2011-11-03T15:06:40.447 回答