std::enable_shared_from_this: 是否允许在析构函数中调用 shared_from_this()?
std::enable_shared_from_this: is it allowed to call shared_from_this() in destructor?
#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;
}
我在调用 shared_from_this()
时收到 std::bad_weak_ptr
异常。是设计使然吗?是的,这可能很危险,因为这个指针不能在析构函数 returns 之后使用,但我看不出为什么在技术上不可能在这里获取指针的原因,因为共享指针对象显然仍然存在并且可以使用。除了编写我自己的 enable_shared_from_this
模拟(我宁愿不这样做)之外,有什么办法可以避免这种情况?
shared_ptr::reset
的实现往往是shared_ptr().swap(*this)
。
这意味着您尝试复制的 shared_ptr
已经处于其析构函数状态,这会在调用您的析构函数之前递减共享计数。当您调用 enable_shared_from_this
时,它将尝试通过从 weak_ptr
构造一个 shared_ptr
来提升存储在其中的 weak_ptr
,当计数为 0 时会导致异常。
所以为了回答你的问题,如果你的标准库实现没有以授权它的方式运行(我不知道它是由标准强制执行还是不是)。
现在,这是一个可以在我的机器上运行的技巧 (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
电话后得到。
[util.smartptr.enab]/7 描述了 shared_from_this
的前提条件:
Requires: enable_shared_from_this<T>
shall be an accessible base class of T
. *this
shall be a subobject of an object t
of type T
. There shall be at least one shared_ptr
instance p
that owns &t
. [emph. added]
既然你的对象正在被销毁,那么肯定没有 shared_ptr
拥有它。因此,您不能在不违反导致未定义行为的要求的情况下调用 shared_from_this
。
I don't see a reason why it would be technically impossible to get the pointer here, since the shared pointer object obviously still exists and can be used.
这是不可能的一个很好的技术原因。
shared_ptr
可能存在,但 A
对象的引用计数已达到零,这就是析构函数 运行 的原因。一旦引用计数达到零,它就不能再次增加(否则你可能会得到一个 shared_ptr
,它引用一个对象,该对象要么在 运行 析构函数的中间,要么已经被销毁了)。
调用 shared_from_this()
尝试增加引用计数和 return 与当前所有者共享所有权的 shared_ptr
,但您不能将计数器从零增加到一个,所以它失败了。
在这种非常特殊的情况下(在对象的析构函数中)你知道对象还没有被完全销毁,但是enable_shared_from_this<A>
无法知道是谁正在调用 shared_from_this()
函数,所以不知道它是发生在 这个非常特殊的情况 中还是在对象析构函数之外的其他代码段中(例如在另一个线程中将在析构函数之后继续执行)。
如果你能以某种方式使它适用于这种特定情况并且你得到一个 shared_ptr<A>
引用当前被销毁的对象,你可以将 shared_ptr
给存储在析构函数之外的东西以备后用。这将允许另一段代码在对象被销毁后访问悬空 shared_ptr
。这将是 shared_ptr
和 weak_ptr
类型系统中的一个大漏洞。
你可以强制允许它,但它有点 "headshoot urself",我无法预测所有后果,但下一个代码按预期工作,允许在 dtors 中调用 shared_from_this() (您也可以用 malloc/free 替换对 boost 的调用):
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;
}
};
#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;
}
我在调用 shared_from_this()
时收到 std::bad_weak_ptr
异常。是设计使然吗?是的,这可能很危险,因为这个指针不能在析构函数 returns 之后使用,但我看不出为什么在技术上不可能在这里获取指针的原因,因为共享指针对象显然仍然存在并且可以使用。除了编写我自己的 enable_shared_from_this
模拟(我宁愿不这样做)之外,有什么办法可以避免这种情况?
shared_ptr::reset
的实现往往是shared_ptr().swap(*this)
。
这意味着您尝试复制的 shared_ptr
已经处于其析构函数状态,这会在调用您的析构函数之前递减共享计数。当您调用 enable_shared_from_this
时,它将尝试通过从 weak_ptr
构造一个 shared_ptr
来提升存储在其中的 weak_ptr
,当计数为 0 时会导致异常。
所以为了回答你的问题,如果你的标准库实现没有以授权它的方式运行(我不知道它是由标准强制执行还是不是)。
现在,这是一个可以在我的机器上运行的技巧 (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
电话后得到。
[util.smartptr.enab]/7 描述了 shared_from_this
的前提条件:
Requires:
enable_shared_from_this<T>
shall be an accessible base class ofT
.*this
shall be a subobject of an objectt
of typeT
. There shall be at least oneshared_ptr
instancep
that owns&t
. [emph. added]
既然你的对象正在被销毁,那么肯定没有 shared_ptr
拥有它。因此,您不能在不违反导致未定义行为的要求的情况下调用 shared_from_this
。
I don't see a reason why it would be technically impossible to get the pointer here, since the shared pointer object obviously still exists and can be used.
这是不可能的一个很好的技术原因。
shared_ptr
可能存在,但 A
对象的引用计数已达到零,这就是析构函数 运行 的原因。一旦引用计数达到零,它就不能再次增加(否则你可能会得到一个 shared_ptr
,它引用一个对象,该对象要么在 运行 析构函数的中间,要么已经被销毁了)。
调用 shared_from_this()
尝试增加引用计数和 return 与当前所有者共享所有权的 shared_ptr
,但您不能将计数器从零增加到一个,所以它失败了。
在这种非常特殊的情况下(在对象的析构函数中)你知道对象还没有被完全销毁,但是enable_shared_from_this<A>
无法知道是谁正在调用 shared_from_this()
函数,所以不知道它是发生在 这个非常特殊的情况 中还是在对象析构函数之外的其他代码段中(例如在另一个线程中将在析构函数之后继续执行)。
如果你能以某种方式使它适用于这种特定情况并且你得到一个 shared_ptr<A>
引用当前被销毁的对象,你可以将 shared_ptr
给存储在析构函数之外的东西以备后用。这将允许另一段代码在对象被销毁后访问悬空 shared_ptr
。这将是 shared_ptr
和 weak_ptr
类型系统中的一个大漏洞。
你可以强制允许它,但它有点 "headshoot urself",我无法预测所有后果,但下一个代码按预期工作,允许在 dtors 中调用 shared_from_this() (您也可以用 malloc/free 替换对 boost 的调用):
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;
}
};