显式调用基类的析构函数/构造函数是否合法?

5

在IT技术中,重置基类已知状态的部分,是否可以销毁和重新构建一个基类对象?

class C : public BaseClass {...};

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

显然,如果我们可以访问类的源代码,就有其他方法来实现这种效果。但是,从语言角度来看,是否存在特定原因使得这种方法无效,这是我想知道的。


6
这是一个适合学术讨论的好问题,但从生产代码的角度来看,这绝对是个糟糕的想法。编写一个名为 ResetBase() 的方法,在基类析构函数中使用它(以避免重复代码),在必要时从派生类中调用它,不要改变析构函数的本意。 - Spook
@Spook 如果 OP 没有类的实现,或者不能或不得更改它,那么这段代码是否可行?也就是说,库的用户是否可以为其类型执行放置 new 操作?当然,前提是相关的构造函数和析构函数是公共的。 - Peter - Reinstate Monica
2
如果BaseClass是可分配的,您可以使用static_cast<BaseClass&>(c) = BaseClass();(或在C++11中,static_cast<BaseClass&>(c) = {};)来实现相同的效果。 - Casey
如果OP没有基类,而且基类也没有重置方法,那么原始类的创建者似乎并不希望该类被重置。在生产代码中,使用与其原始创建者想法相悖的类是绝对不希望发生的事情。 - Spook
我的朋友面对这样的想法时总是毫不犹豫地回答:“设计不好”。实际上,在98%的情况下,他都是正确的。 - Spook
显示剩余3条评论
2个回答

8
不,这是不合法的。您不能替换对象的基本子对象。
C++11 3.8/7指定,如果原对象是类型T的最派生对象(1.8),并且新对象是类型T的最派生对象(即,它们不是基类子对象),则只能重用对象的存储。
您要替换的对象是一个基类子对象,而不是最派生对象,因此是被禁止的。
如果您替换整个对象(即调用~C,然后构造一个新的C),那么这是合法的,但是很危险。如果构造函数抛出异常,则对象将在其生命周期结束时第二次被销毁。这会产生未定义的行为。

@MikeSeymour 然而,只有基类的析构函数被调用。我理解 OP 正在尝试“重新初始化”基类子对象,而不是销毁并重新初始化整个最派生对象。 - Angew is no longer proud of SO
调用BaseClass构造函数只对对象的一部分进行操作,该部分与常规的BaseClass对象无法区分。 - Brice M. Dempsey
2
@JamesT.Huggett:尽管如此,这条规则禁止这样做。你只能替换最派生的对象,而不能替换基类子对象。 - Mike Seymour
@JamesT.Huggett,正如我在我的(已删除的)答案中提到的那样,C 可能具有指向 BaseClass 分配的资源的指针,这就是 UB 的原因。 - Anton Savin
我认为你已经明白了。尽管我不同意我正在用一个不同类型的对象替换另一个对象。关键是你不能替换子对象。 - Brice M. Dempsey
显示剩余4条评论

2
析构函数只有在声明为虚函数时才会生效,否则你将会得到一个部分销毁的对象(根据你的评论似乎这正是你想要的,但这是不合法的)。放置 new 应该能够工作,但我很确定它实际上不被标准所允许(尽管我认为它看起来是有效的)。我不确定你为什么想要这样做,因为对象曾经是派生类 C,但在构造后它永远不会再是 C,只会是 BaseClass

这个想法是重置基础对象的状态,而不触及派生类中添加的字段。我不确定你所说的“成为C”是什么意思。 - Brice M. Dempsey
为什么虚析构函数很重要?我明确调用了我想要的析构函数。 - Brice M. Dempsey
1
“be a C” 指的是你的 class C。你在这里做的事情完全是错误的,而且不会起作用。如果 BaseClass 的析构函数是虚拟的,那么 C 的析构函数将被调用,并且对象将被完全销毁,尽管内存不会被释放,因为你没有调用 delete。如果它不是虚拟的,也许你只会破坏 BaseClass 的位,但我怀疑它能够保证工作。与其这样做,不如在你的 BaseClass 上制作一个简单的 reset() 方法。 - John Zwinck
1
@JohnZwinck 调用基类的析构函数 BaseClass::~BaseClass 将防止虚拟分派。 - Simple
@Simple:啊,你说得对,我没有注意到里面的显式 ::,是我的失误。无论如何,这段代码不能保证按预期工作。 - John Zwinck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接