C++对象销毁时通过this访问成员调用虚函数是否安全?
析构阶段通过this访问成员的安全性问题
我们试图实现一种RAII机制,在对象销毁时自动触发特定操作,最初的实现代码如下:
struct Finalizer { std::function<void()> fun; // 析构时调用此函数 ~Finalizer() { fun(); } Finalizer(const Finalizer&) = delete; Finalizer(Finalizer&&) = delete; Finalizer& operator=(const Finalizer&) = delete; Finalizer& operator=(Finalizer&&) = delete; }; struct Iface { virtual void foo() = 0; }; struct Class { Iface& iface; // 保证在"fin"之后销毁 Finalizer fin{[this] { iface.foo(); }}; // 这里是否始终安全? };
对应的使用示例:
struct IfaceImpl : Iface { void foo() final { std::cout << "Foo!\n"; } }; int main() { IfaceImpl impl; Class ty{impl}; }
原本认为这段代码是安全的——因为fin是后初始化的成员,会先被销毁,此时iface引用的生命周期还未结束。但在GCC 14和Clang 18中运行时会出现段错误,只有改成直接捕获成员变量的形式才能正常工作:
Finalizer fin{[&ifc = this->iface] { ifc.foo(); }};
核心原因分析
问题的本质在于对象生命周期的终止时机:根据C++标准,当对象的析构函数开始执行时,该对象的生命周期就已经结束了,即便它的成员还没有全部完成销毁。
在Class对象的析构流程中,虽然fin的析构先执行(成员销毁顺序与初始化顺序相反),但此时Class对象本身已经进入析构阶段,this指针指向的是一个已经结束生命周期的对象。此时通过this->iface访问成员属于未定义行为——哪怕iface引用本身的生命周期还没结束,编译器也可能因为对象已“死亡”而进行优化(比如提前释放对象内存、标记对象无效),导致访问出错。
而改成直接捕获this->iface的引用时,我们捕获的是成员变量本身的引用,而非通过this间接访问。由于iface的销毁在fin之后,此时该引用仍然有效,调用foo()的操作是安全的。
内容的提问来源于stack exchange,提问作者Pavel Kirienko
相关产品推荐
相关产品推荐

