Ad *_*d N 89 c++ smart-pointers shared-ptr unique-ptr
问题确实符合标题:我很想知道这种差异的技术原因是什么,还有理由?
std::shared_ptr<void> sharedToVoid; // legal;
std::unique_ptr<void> uniqueToVoid; // ill-formed;
Run Code Online (Sandbox Code Playgroud)
Naw*_*waz 105
这是因为std::shared_ptr
实现类型擦除,而std::unique_ptr
不是.
由于std::shared_ptr
实现了类型擦除,它还支持另一个有趣的属性,即.它并没有需要删除器的类型为模板类型参数的类模板.看看他们的声明:
template<class T,class Deleter = std::default_delete<T> >
class unique_ptr;
Run Code Online (Sandbox Code Playgroud)
其中包含Deleter
类型参数
template<class T>
class shared_ptr;
Run Code Online (Sandbox Code Playgroud)
没有它.
现在的问题是,为什么要shared_ptr
实现类型擦除?好了,它这样做,因为它具有以支持引用计数,并支持这一点,它必须从堆中分配内存和,因为它具有反正分配内存,它更进一步并实现类型擦除-需要堆分配也是.所以基本上它只是机会主义者!
由于类型擦除,std::shared_ptr
能够支持两件事:
void*
,但它仍然能够通过正确调用它们的析构函数来正确删除对象.好的.这就是如何std::shared_ptr
运作.
现在的问题是,可以std::unique_ptr
将对象存储为 void*
?嗯,答案是肯定的 - 如果你传递一个合适的删除器作为参数.这是一个这样的演示:
int main()
{
auto deleter = [](void const * data ) {
int const * p = static_cast<int const*>(data);
std::cout << *p << " located at " << p << " is being deleted";
delete p;
};
std::unique_ptr<void, decltype(deleter)> p(new int(959), deleter);
} //p will be deleted here, both p ;-)
Run Code Online (Sandbox Code Playgroud)
输出(在线演示):
959 located at 0x18aec20 is being deleted
Run Code Online (Sandbox Code Playgroud)
你在评论中提出了一个非常有趣的问题:
在我的情况下,我将需要一个类型擦除删除器,但它似乎也可能(以一些堆分配为代价).基本上,这是否意味着实际上存在第三类智能指针的利基点:具有类型擦除的独占所有权智能指针.
到@Steve杰索普提出了以下解决方案,
我从来没有真正试过这个,但也许你可以通过使用适当
std::function
的删除类型来实现这一点unique_ptr
?假设实际上有效,那么你就完成了,独占所有权和类型删除的删除器.
根据这个建议,我实现了这个(虽然它没有使用,std::function
因为它似乎没有必要):
using unique_void_ptr = std::unique_ptr<void, void(*)(void const*)>;
template<typename T>
auto unique_void(T * ptr) -> unique_void_ptr
{
return unique_void_ptr(ptr, [](void const * data) {
T const * p = static_cast<T const*>(data);
std::cout << "{" << *p << "} located at [" << p << "] is being deleted.\n";
delete p;
});
}
int main()
{
auto p1 = unique_void(new int(959));
auto p2 = unique_void(new double(595.5));
auto p3 = unique_void(new std::string("Hello World"));
}
Run Code Online (Sandbox Code Playgroud)
输出(在线演示):
{Hello World} located at [0x2364c60] is being deleted.
{595.5} located at [0x2364c40] is being deleted.
{959} located at [0x2364c20] is being deleted.
Run Code Online (Sandbox Code Playgroud)
希望有所帮助.
其中一个基本原理是a的许多用例之一shared_ptr
- 即作为终身指标或哨兵.
原始的boost文档中提到了这一点:
auto register_callback(std::function<void()> closure, std::shared_ptr<void> pv)
{
auto closure_target = { closure, std::weak_ptr<void>(pv) };
...
// store the target somewhere, and later....
}
void call_closure(closure_target target)
{
// test whether target of the closure still exists
auto lock = target.sentinel.lock();
if (lock) {
// if so, call the closure
target.closure();
}
}
Run Code Online (Sandbox Code Playgroud)
哪里closure_target
是这样的:
struct closure_target {
std::function<void()> closure;
std::weak_ptr<void> sentinel;
};
Run Code Online (Sandbox Code Playgroud)
调用者会注册一个类似这样的回调:
struct active_object : std::enable_shared_from_this<active_object>
{
void start() {
event_emitter_.register_callback([this] { this->on_callback(); },
shared_from_this());
}
void on_callback()
{
// this is only ever called if we still exist
}
};
Run Code Online (Sandbox Code Playgroud)
因为shared_ptr<X>
始终可以转换为shared_ptr<void>
,event_emitter现在可以幸福地不知道它正在回调的对象类型.
这种安排释放订户处理交叉案件的义务的情况下,发射器(如果什么而active_object消失,要操作的回调在队列中,等待?),也意味着没有必要进行同步退订.weak_ptr<void>::lock
是同步操作.