std::enable_shared_from_this:是否允许在析构函数中调用shared_from_this()?

问题描述 投票:0回答:5
#include <memory>
#include <iostream>

struct A : public std::enable_shared_from_this<A>
{
    ~A()
    {
        auto this_ptr = shared_from_this(); // std::bad_weak_ptr exception here. 
        std::cout << "this: " << this_ptr;
    }
};

int main()
{
    auto a = std::make_shared<A>();
    a.reset();
    return 0;
}

我在调用

std::bad_weak_ptr
时遇到
shared_from_this()
异常。是设计使然吗?是的,这可能很危险,因为在析构函数返回后无法使用该指针,但我看不出技术上不可能在此处获取指针的原因,因为共享指针对象显然仍然存在并且可以用过的。有什么办法可以绕过这个,除了编写我自己的
enable_shared_from_this
类似物(我宁愿不这样做)?

c++ c++11 destructor shared-ptr weak-ptr
5个回答
25
投票

我不明白为什么在技术上不可能在这里获取指针,因为共享指针对象显然仍然存在并且可以使用。

有一个很好的技术原因可以解释为什么这是不可能的。

shared_ptr
可能存在,但
A
对象的引用计数已达到零,这就是析构函数正在运行的原因。一旦引用计数达到零,它就不能再次增加(否则你可能会得到一个
shared_ptr
,它引用一个正在运行其析构函数的对象,或者已经被销毁)。

调用

shared_from_this()
尝试增加引用计数并返回与当前所有者共享所有权的
shared_ptr
,但您无法将计数器从 0 增加到 1,因此会失败。

这个非常具体的情况(在对象的析构函数内),您知道该对象尚未完全销毁,但是

enable_shared_from_this<A>
无法知道谁在调用
shared_from_this()
函数,因此无法知道是否它发生在“这个非常具体的情况”或对象析构函数之外的其他一些代码中(例如,在析构函数之后继续运行的另一个线程中)。 如果你能以某种方式让它适用于这个特定的情况,并且你得到一个

shared_ptr<A>

引用当前被销毁的对象,你可以将

shared_ptr
给析构函数之外的东西来存储它以供以后使用。这将允许另一段代码在对象被销毁后访问悬空的
shared_ptr
。这将是
shared_ptr
weak_ptr
类型系统中的一个大洞。
    


12
投票
shared_from_this

的前提条件:


要求:

enable_shared_from_this<T> 应是

T
的可访问基类。
*this
应是类型为
t
的对象
T
的子对象。
至少应有一个 
shared_ptr 实例
p
拥有
&t
 [emph.添加]

既然你的对象正在被销毁,那么一定是没有
shared_ptr

拥有它。因此,您无法在不违反该要求的情况下调用

shared_from_this
,从而导致未定义的行为。
    


1
投票
shared_ptr::reset

的实现往往是

shared_ptr().swap(*this)

这意味着您尝试复制的

shared_ptr

已经处于析构函数状态,这会在调用析构函数之前减少共享计数。当您调用

enable_shared_from_this
时,它将尝试通过从
weak_ptr
构造
shared_ptr
来提升存储在其中的
weak_ptr
,这会在计数为 0 时导致异常。

因此,为了回答您的问题,如果您的标准库实现没有以授权的方式运行(我不知道它是否是由标准强制执行的),则没有标准的方法可以做您想做的事情。

现在,这是一个可以在我的机器上运行的 hack(clang/libc++):

#include <memory> #include <iostream> class hack_tag { }; namespace std { template<> class shared_ptr<hack_tag> { public: template<typename T> weak_ptr<T> extract_weak(const enable_shared_from_this<T>& shared) { return shared.__weak_this_; } }; }; using weak_ptr_extractor = std::shared_ptr<hack_tag>; class test : public std::enable_shared_from_this<test> { public: test() { std::cout << "ctor" << std::endl; } ~test() { std::cout << "dtor" << std::endl; weak_ptr_extractor hacker; auto weak = hacker.extract_weak(*this); std::cout << weak.use_count() << std::endl; auto shared = weak.lock(); } }; int main(void) { std::shared_ptr<test> ptr = std::make_shared<test>(); ptr.reset(); }

但我不确定你能用它做任何有用的事情,因为你复制的 
shared_ptr

即将消失,并且该副本不会与

shared_ptr
调用后获得的新干净
reset
共享内容.
    


0
投票

template<class GenT, typename... Args> struct AllocSharedObj { static std::shared_ptr<GenT> alloc(Args&&... args) { using pool_t = boost::singleton_pool<GenT, sizeof(GenT)>; void *mem = pool_t::malloc(); //log_create_delete(true); auto r = std::shared_ptr<GenT>(new (mem) GenT(std::forward<Args>(args)...), [](GenT * p) { if (p) { //log_create_delete(false); //dirty hack, allowing to call SHARED_FROM_THIS inside that functions >: auto cheat = std::shared_ptr<GenT>(p, [](auto) {}); p->~GenT(); cheat = nullptr; pool_t::free(p); } }); //here can be post-constructor init which needs shared_from_this like r->init(); return r; } };



0
投票

微软 std impl 的区别在于,weak ptr 是 _Wptr 而不是 _

weak_this

我的用例是,在我有消费者/生产者关系的情况下,我想用弱指针完全替换原始指针。当生产者超出范围时,在它的最后一段(在它的析构函数中)将通知听众它的消亡。

© www.soinside.com 2019 - 2024. All rights reserved.