使用enabled_shared_from_this时似乎存在一些边缘情况。例如:
boost shared_from_this and multiple inheritance
可以在不需要enable_shared_from_this的情况下实现shared_from_this吗?如果是这样,是否可以快速制作?
答案 0 :(得分:7)
shared_ptr
是3件事。它是一个参考计数器,一个驱逐舰和一个拥有的资源。
当你make_shared
时,它会同时分配所有3个,然后在那个块中构造它们。
当您从shared_ptr<T>
创建T*
时,您需要单独创建引用计数器/驱逐舰,并注意所拥有的资源是T*
。
shared_from_this
的目标是我们可以从shared_ptr<T>
基本上提取T*
(假设它存在)。
如果所有共享指针都是通过make_shared
创建的,那么这很容易(除非您希望在失败时定义行为),因为布局很容易。
但是,并非所有共享指针都是以这种方式创建的。有时您可以创建一个指向未由任何std
库函数创建的对象的共享指针,因此T*
与共享指针引用计数和销毁数据无关。
由于T*
中没有 room 或者它(通常)指向这样的构造,我们必须将其存储在外部,这意味着全局状态和线程安全开销和其他痛苦。对于不需要shared_from_this
的人来说,这将是一种负担,与需要它的人(互斥体,查找等)的当前状态相比,性能会受到影响。
当前设计在weak_ptr<T>
中存储enable_shared_from_this<T>
。每当调用weak_ptr
或make_shared
ctor时,都会初始化shared_ptr<T>
。现在我们可以从shared_ptr<T>
创建一个T*
,因为我们通过继承enable_shared_from_this<T>
在课堂上“腾出空间”。
这又是极低的成本,并且很好地处理了简单的情况。我们最终会在weak_ptr<T>
的基准成本上花费一T
的开销。
当您有两个不同的shared_from_this
时,他们的weak_ptr<A>
和weak_ptr<B>
成员是不相关的,因此在您想要存储生成的智能指针(可能两者都是?)时不明确。这种歧义导致您看到的错误,因为它假设在一个唯一weak_ptr<?>
中只有一个shared_from_this<?>
成员,实际上有两个。
The linked solution提供了一种灵活的方法来扩展它。它写enable_shared_from_this_virtual<T>
。
这里不存储weak_ptr<T>
,而是存储weak_ptr<Q>
,其中Q
是enable_shared_from_this_virtual<T>
的虚拟基类,并且在虚拟基类中唯一存储。然后它非虚拟地覆盖shared_from_this
和类似的方法,以提供与shared_from_this<T>
使用“成员指针或子类型shared_ptr
构造函数”相同的接口,在此处拆分引用计数/驱逐舰来自拥有的资源组件的组件,以类型安全的方式。
此处的开销大于基本shared_from_this
:它具有虚拟继承并强制使用虚拟析构函数,这意味着该对象存储指向虚函数表的指针,并且对shared_from_this
的访问速度较慢因为它需要虚拟功能表调度。
优点是它“正常”。 heirarchy现在有一个唯一的shared_from_this<?>
,你仍然可以获得继承自T
的类shared_from_this<T>
的类型安全共享指针。
答案 1 :(得分:0)
是的,它可以使用
类型的全局哈希表unordered_map< T*, weak_ptr<T> >
从this
执行共享指针的查找。
#include <memory>
#include <iostream>
#include <unordered_map>
#include <cassert>
using namespace std;
template<class T>
struct MySharedFromThis {
static unordered_map<T*, weak_ptr<T> > map;
static std::shared_ptr<T> Find(T* p) {
auto iter = map.find(p);
if(iter == map.end())
return nullptr;
auto shared = iter->second.lock();
if(shared == nullptr)
throw bad_weak_ptr();
return shared;
}
};
template<class T>
unordered_map<T*, weak_ptr<T> > MySharedFromThis<T>::map;
template<class T>
struct MyDeleter {
void operator()(T * p) {
std::cout << "deleter called" << std::endl;
auto& map = MySharedFromThis<T>::map;
auto iter = map.find(p);
assert(iter != map.end());
map.erase(iter);
delete p;
}
};
template<class T>
shared_ptr<T> MyMakeShared() {
auto p = shared_ptr<T>(new T, MyDeleter<T>());
MySharedFromThis<T>::map[p.get()] = p;
return p;
}
struct Test {
shared_ptr<Test> GetShared() { return MySharedFromThis<Test>::Find(this); }
};
int main() {
auto p = MyMakeShared<Test>();
assert(p);
assert(p->GetShared() == p);
}
但是,只要从T *构造shared_ptr,并且在调用删除器之前,就必须更新映射,从而节省时间。此外,为了线程安全,互斥锁必须保护对映射的访问,在线程之间序列化相同类型的分配。因此,此实现的效果不如enable_shared_from_this
。
<强>更新强>
使用make_shared使用的相同指针技巧对此进行改进,这里的实现应该与shared_from_this一样快。
template<class T>
struct Holder {
weak_ptr<T> weak;
T value;
};
template<class T>
Holder<T>* GetHolder(T* p) {
// Scary!
return reinterpret_cast< Holder<T>* >(reinterpret_cast<char*>(p) - sizeof(weak_ptr<T>));
}
template<class T>
struct MyDeleter
{
void operator()(T * p)
{
delete GetHolder(p);
}
};
template<class T>
shared_ptr<T> MyMakeShared() {
auto holder = new Holder<T>;
auto p = shared_ptr<T>(&(holder->value), MyDeleter<T>());
holder->weak = p;
return p;
}
template<class T>
shared_ptr<T> MySharedFromThis(T* self) {
return GetHolder(self)->weak.lock();
}