std::unique_ptr,皮条和对象生存期
std::unique_ptr, pimpl and object lifetime
以下示例在Linux(GNU STL(上使用gcc 11编译,在FreeBSD(clang STL(上同时使用clang 12编译。在Linux上,它运行并打印值1和2。在FreeBSD上,它打印值1,然后用SEGV崩溃。我不太了解对象的生存期,所以整个过程可能是UB,运行时行为可能不相关。我知道std::unique_ptr
在这两个STL之间的实现有一个重要的不同:Clang STL在析构函数开始时将std::unique_ptr
的内部指针重置为nullptr
,而GNU STL则不使用指针。
#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
的生命即将结束 - 析构函数CCD_ 6运行。一旦析构函数的主体(默认(完成,对象的生存期就结束了,使用该对象就是UB
- 现在运行对象的子对象(成员对象?(的析构函数
- 则析构函数CCD_ 7运行。它为持有的对象运行析构函数
- 析构函数CCD_ 8使用指向不再活动的对象CCD_
如果能给出一个答案,指出对对象寿命的理解是否正确,我将不胜感激。
如果它不是UB,那么就有一个单独的实现质量问题(或者我应该在调用d指针上的方法之前检查它,但这对一个pimpl来说似乎有点麻烦;然后我们得到一个STL实现所需的if(d)d->cleanup()
,而在另一个STL实现中这是一个无用的检查(。
为了提出一个问题:在对象c
的销毁过程中,此代码是否在语句m_owner->cleanup()
(第9行(中显示UB?
是的,m_owner
引用的对象的生存期已经结束,当调用m_owner->cleanup();
时,它的析构函数调用完成。因此,呼叫为UB。
相关文章:
- 在不复制临时对象的情况下延长其生存期
- 结束另一个线程中使用的对象的生存期
- "this"指针的值在对象的生存期内是否恒定?
- 数组对象的生存期是否在重用其元素存储时结束?
- 具有空洞初始化的对象的生存期
- 如何在向量列表初始化时避免对象复制以及如何延长临时的生存期
- 子表达式中临时对象的生存期
- 对临时对象的Const引用不会延长其生存期
- 对象存在与对象生存期不同吗
- 指向对象生存期之外的已分配内存的指针是"invalid pointer[s]"还是"pointer[s] to an object"?
- "std::function"的简单版本:函数对象的生存期?
- 有什么方法可以延长C++中临时对象的生存期吗
- 以延长构造函数外部 QT 对象的生存期
- QML QQmlPropertyList - 包含的对象生存期和'memory rules'
- 在函数调用中C++临时对象的生存期
- std::tie 和元组中返回的对象的生存期
- 是否存在对象存储在其生存期内可能会更改的情况?
- 从“if constexpr”分支扩展对象生存期/范围
- 在函数调用中创建的对象的生存期
- thread_local 和 std::future 对象 - 对象的生存期是多少