#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
类似物(我宁愿不这样做)?
我不明白为什么在技术上不可能在这里获取指针,因为共享指针对象显然仍然存在并且可以使用。
有一个很好的技术原因可以解释为什么这是不可能的。
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
类型系统中的一个大洞。shared_from_this
的前提条件:
要求:
enable_shared_from_this<T>
应是的可访问基类。T
应是类型为*this
的对象t
的子对象。T
至少应有一个shared_ptr
实例拥有p
。&t
[emph.添加]既然你的对象正在被销毁,那么一定是没有
shared_ptr
拥有它。因此,您无法在不违反该要求的情况下调用
shared_from_this
,从而导致未定义的行为。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
共享内容.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;
}
};
微软 std impl 的区别在于,weak ptr 是 _Wptr 而不是 _
weak_this。 我的用例是,在我有消费者/生产者关系的情况下,我想用弱指针完全替换原始指针。当生产者超出范围时,在它的最后一段(在它的析构函数中)将通知听众它的消亡。