通过放置 new 来延迟真正的基类构建

发布于 2024-12-13 15:26:04 字数 930 浏览 3 评论 0原文

我问以下方法是否(以及为什么)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()。怪我低血糖!

I'm asking if (and why) the following approach is a) legal and b) moral. I'm asking with emphasis on C++03, but notes on C++11 are welcome, too. The idea is to prevent derived classes that could themselves be default constructible from implementing stupid B::B(int foo) : A(foo) {} constructors.

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;
}

My gut is telling me, that something is fishy here. If any Derived class accesses Base members in its constructor, I want to be somewhere else, but otherwise I have trouble seeing valid reasons why the approach is bad.

Anyway, if you think this is an acceptable approach, how would to deal with reference members (something like int& Base::j)?

Note: This is a follow-up question to How can I fake constructor inheritance in C++03?.


Edit: I must have been distracted when posting the question. Of course, instead of delete b I meant b->~Base(). I blame low blood sugar!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

灵芸 2024-12-20 15:26:04

该代码不正确并触发未定义的行为。您的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;
}

如果没有删除,因此特定的未定义行为源消失了,代码仍然是未定义行为(可能有太多方式甚至无法提及) 。一旦对析构函数的调用仍然是 UB,即使事实并非如此,您重新创建了 Base 类型这一事实意味着该对象的动态分派可能会将该对象视为 Base 而不是 Derived 对象(在 vtable 的情况下,指向运行时类型信息的 vptr 将引用到 Base,而不是派生

并且可能还有两三个其他的事情可能会出错,我现在无法想到......

The code is incorrect and triggers undefined behavior. Your Base class does not have a virtual destructor, which means that the delete b will cause Undefined Behavior.

The reasons for UB in the call to delete range from the fact that it will not release the derived resources (which seems to be the purpose of the code, ouch!), to the fact that it will try to release the allocated memory, which might work or not, depending on the layout of both objects. If it fails to deallocate the memory it will probably crash, if it succeeds the placement new call in the following line will try to initialize an object in memory that has already been released...

Even if you changed the code (trying to avoid deallocation problems) to:

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;
}

Where there is no delete and thus that particular source of Undefined Behavior is gone, the code is still undefined behavior (probably in too many ways to even mention). For once the call to the destructor is still UB, even if that was not, the fact that you recreated the Base type means that dynamic dispatch for that object will probably consider the object to be a Base rather than a Derived object (in the case of vtables, the vptr that points to the RunTime Type information will refer to Base, rather than Derived)

And there are probably two or three other things that can go wrong and I cannot think of right now...

装迷糊 2024-12-20 15:26:04

delete b 不仅仅调用 Base 的析构函数,它还释放 new 返回的内存并调用 Derived s析构函数[假设您希望 Base 有一个虚拟析构函数...如果您希望它是非虚拟的,则该行为一开始就未定义]。这意味着您随后使用placement new 会在内存中构造一个不再有效的全新Base 对象,并且您永远不会替换之前销毁的Derived 部分。简而言之,你所做的一切都没有接近正确的行为。

坦率地说,我不明白你想做什么......为什么必须默认构造 Derived ,而不是仅仅转发参数?这并不愚蠢,而是做事的方式。

delete b does not just call Bases destructor, it also deallocates the memory returned by new and calls Deriveds destructor [assuming you intended Base to have a virtual destructor... if you intended it to be non-virtual, the behavior is simply undefined to begin with]. Which means your subsequent use of placement new is constructing a whole new Base object in memory that is no longer valid, and you are never replacing the portion of Derived that you destroyed earlier. In short, nothing you have done even approaches correct behavior.

Frankly, I don't see what you're trying to do... why must Derived be default constructed, instead of just forwarding an argument? Its not stupid, its the way things are done.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文