【问题标题】:Is it legal to explicitly call base class destructor/constructor?显式调用基类析构函数/构造函数是否合法?
【发布时间】:2014-10-23 09:20:26
【问题描述】:

破坏和构造基类对象以重置基类已知的状态部分是否合法?

class C : public BaseClass {...};

C c;
c.BaseClass::~BaseClass();
new (static_cast<BaseClass*>(&c)) BaseClass;

如果我们可以访问类的源代码,显然还有其他方法可以实现这种效果。但是,我想从语言的角度了解这是否无效。

【问题讨论】:

  • 没有。首先,不能保证&amp;c 与其基类子对象的地址相同。
  • 公平点。我添加了演员表。
  • 基类中的reconstruct 方法不行吗?这听起来像是一个 X-Y 问题。
  • 这是学术讨论的好问题,但从生产代码的角度来看绝对是糟糕的想法。编写一个方法ResetBase(),在基析构函数中使用它(以避免代码加倍),如果需要从派生类调用,不要以不是为它们创建的方式与析构函数混淆。
  • 如果BaseClass是可赋值的,你可以用static_cast&lt;BaseClass&amp;&gt;(c) = BaseClass();(或c++11中的static_cast&lt;BaseClass&amp;&gt;(c) = {};)达到同样的效果。

标签: c++ inheritance constructor destructor


【解决方案1】:

不,这是不合法的。不允许替换对象的基础子对象。

C++11 3.8/7 指定只有在以下情况下才能重用对象的存储

原始对象是 T 类型的最派生对象 (1.8),而新对象是 T 类型的最派生对象(也就是说,它们不是基类子对象)。

您替换的对象是基类子对象,而不是最派生的对象,因此被禁止。

如果你要替换整个对象(即调用~C,然后构造一个新的C),那么这将是合法的,但很危险。如果构造函数抛出,那么对象将在其生命周期结束时第二次被销毁。这会产生未定义的行为。

【讨论】:

  • 你能解释一下是什么阻止了对象是 C 类型的吗?我不是要替换或更改对象,只是重新初始化它的基类区域。
  • @JamesT.Huggett: new (...) BaseClass 创建一个 BaseClass 类型的对象,而不是 C
  • @MikeSeymour 但是,只调用了基类析构函数。我了解 OP 正在尝试“重新初始化”基类子对象而不破坏重新初始化整个最派生的对象。
  • 调用 BaseClass 构造函数只对与常规 BaseClass 对象无法区分的对象的一部分进行操作。
  • @JamesT.Huggett:无论如何,这条规则是禁止的。您只能替换最派生的对象,不能替换基类子对象。
【解决方案2】:

析构函数(仅)在它是虚拟的情况下才起作用,否则您将拥有一个部分销毁的对象(这似乎是您的 cmets 实际想要的,但不合法)。新的位置应该可以工作,但我很确定标准并没有真正允许(尽管我认为它看起来很有可能会起作用)。而且我不确定您为什么想要这个,因为该对象曾经是派生的C,但在构造之后它再也不会是C,只有BaseClass

【讨论】:

  • 这个想法是在不触及派生类中添加的字段的情况下重置基础对象的状态。我不确定您所说的“成为 C”是什么意思。
  • 为什么虚拟析构函数是相关的?我明确地调用了我想要的析构函数。
  • “成为 C”指的是您的 class C。你在这里做的是完全错误的,不会奏效。如果BaseClass dtor 是虚拟的,C 析构函数将被调用,并且对象将被完全销毁,尽管内存不会被释放,因为您没有调用delete。如果它不是虚拟的,也许你只会破坏BaseClass 的位,但我怀疑它是否能保证工作。而不是所有这些,只需在您的 BaseClass 上创建一个简单的 reset() 方法。
  • @JohnZwinck 使用BaseClass::~BaseClass 调用基类的析构函数将阻止虚拟调度。
  • @Simple:啊,你是对的,我没有在其中看到明确的::,我的错。无论如何,不​​能保证代码按预期工作。
猜你喜欢
  • 2011-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-19
  • 2018-07-21
  • 2014-04-15
  • 2016-06-12
相关资源
最近更新 更多