下面的示例在Linux上编译gcc 11 (GNU ),在FreeBSD上编译clang 12 (Clang )。在Linux上,它运行并打印值1和2。在FreeBSD上,它打印值1,然后使用SEGV崩溃。我不认为完全理解对象的生存期--因此,整个过程可能是UB,运行时行为可能与此无关。我确实知道,这两个STL之间的implementation of std::unique_ptr有一个重要的区别: Clang在析构函数开始时重置std::unique_ptr到nullptr的内部指针,而GNU只保留指针。
#include <iostream>
#include <memory>
struct C {
struct Private {
C* m_owner;
int m_x;
Private(C* owner) : m_owner(owner), m_x(0) {}
~Private() { m_owner->cleanup(); }
void cleanup() { std::cout << "Private x=" << ++m_x << '\n'; }
};
std::unique_ptr<Private> d;
C() { d = std::make_unique<Private>(this); }
~C() = default;
void cleanup() { d->cleanup(); }
};
int main(int argc, char **argv)
{
C c;
c.cleanup(); // For display purposes, print 1
return 0; // Destructors called, print 2
}FreeBSD上的输出:
Private x=1
Segmentation fault (core dumped)还有一段回溯:
* thread #1, name = 'a.out', stop reason = signal SIGSEGV: invalid address (fault address: 0x8)
frame #0: 0x00000000002032b4 a.out`C::Private::cleanup() + 52
a.out`C::Private::cleanup:
-> 0x2032b4 <+52>: movl 0x8(%rax), %esi我认为这可能是UB的原因是:
return 0,c的生命即将结束,~C(),运行。一旦完成了析构函数的主体(默认),对象的生存期就结束了,并且使用该对象的是UB。~std::unique_ptr<Private>正在运行。它为所保存的对象运行析构函数。析构函数~Private()使用指向已不存在的对象m_owner的指针来调用成员函数.。
如果对对象生命周期的理解是正确的,我将希望得到一个答案。
如果不是UB,那么就有一个单独的实现质量问题(或者我应该在调用方法之前检查d-指针,但是对于pimpl来说,这似乎有点不正常;然后我们得到一个STL实现所需要的if(d)d->cleanup(),在另一个STL实现中这是一个无用的检查)。
为了提出一个问题:在销毁对象m_owner->cleanup()期间,该代码是否在语句c (第9行)中显示UB?
发布于 2022-01-24 10:33:37
是的,m_owner引用的对象的生存期已经结束,当调用m_owner->cleanup();时,它的析构函数调用已经完成。因此,呼叫是UB。
https://stackoverflow.com/questions/70832270
复制相似问题