Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/158.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么要共享\u ptr<;无效>;合法,但独特\u ptr<;无效>;这是病态的吗?_C++_Shared Ptr_Smart Pointers_Unique Ptr - Fatal编程技术网

C++ 为什么要共享\u ptr<;无效>;合法,但独特\u ptr<;无效>;这是病态的吗?

C++ 为什么要共享\u ptr<;无效>;合法,但独特\u ptr<;无效>;这是病态的吗?,c++,shared-ptr,smart-pointers,unique-ptr,C++,Shared Ptr,Smart Pointers,Unique Ptr,这个问题真的很适合这个标题:我很想知道造成这种差异的技术原因是什么,还有理由是什么 std::shared_ptr<void> sharedToVoid; // legal; std::unique_ptr<void> uniqueToVoid; // ill-formed; std::shared\ptr sharedToVoid;//合法的 std::unique_ptr uniqueToVoid;//畸形的; 这是因为std::shared_ptr实现了类型擦除,

这个问题真的很适合这个标题:我很想知道造成这种差异的技术原因是什么,还有理由是什么

std::shared_ptr<void> sharedToVoid; // legal;
std::unique_ptr<void> uniqueToVoid; // ill-formed;
std::shared\ptr sharedToVoid;//合法的
std::unique_ptr uniqueToVoid;//畸形的;

这是因为
std::shared_ptr
实现了类型擦除,而
std::unique_ptr
没有


由于
std::shared_ptr
实现了类型擦除,因此它还支持另一个有趣的属性,即。它不需要类模板的deleter as template type参数的类型。看看他们的声明:

template<class T,class Deleter = std::default_delete<T> > 
class unique_ptr;
没有

现在的问题是,
shared\u ptr
为什么要实现类型擦除?好的,它这样做了,因为它必须支持引用计数,为了支持这一点,它必须从堆中分配内存,而且因为它无论如何都必须分配内存,所以它更进一步,实现类型擦除——这也需要堆分配。所以基本上这只是机会主义

由于类型擦除,
std::shared_ptr
能够支持两件事:

  • 它可以将任何类型的对象存储为
    void*
    ,但是它仍然能够通过正确地调用对象的析构函数来正确地删除销毁时的对象
  • deleter的类型不会作为类型参数传递给类模板,这意味着在不影响类型安全的情况下有一点自由
好的。这就是所有关于std::shared_ptr
的工作原理

现在的问题是,
std::unique\u ptr
能否将对象存储为
void*
?好的,答案是,是的——只要你传递一个合适的deleter作为参数。这里有一个这样的例子:

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 ;-)

您在评论中提出了一个非常有趣的问题:

在我的例子中,我需要一个类型擦除删除器,但它似乎也是可能的(以一些堆分配为代价)。基本上,这是否意味着第三种类型的智能指针实际上有一个合适的位置:具有类型擦除功能的独占所有权智能指针

对此提出了以下解决方案

我从未真正尝试过这一点,但也许您可以通过使用适当的
std::function
作为带有
unique\u ptr
的deleter类型来实现这一点?假设这确实有效,那么您就完成了,独占所有权和类型擦除删除器

根据这个建议,我实现了这个(尽管它没有使用
std::function
,因为它似乎没有必要):


希望能有所帮助。

其中一个基本原理是在
共享\u 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();
    }
}
调用方将注册一个回调,如下所示:

struct closure_target {
    std::function<void()> closure;
    std::weak_ptr<void> sentinel;
};
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 
    }
};
struct active\u对象:std::从\u中启用\u共享\u
{
void start(){
事件_发射器u.register _回调([this]{this->on _回调();}),
从_this()共享_;
}
在_回调()上无效
{
//只有在我们仍然存在的情况下,才会调用此函数
}
};
由于
shared_ptr
始终可转换为
shared_ptr
,因此事件发射器现在可以很高兴地不知道它要回调的对象的类型


这种安排使事件发射器的订阅者免除了处理交叉情况的义务(如果队列中的回调在活动对象离开时等待操作,该怎么办?),也意味着不需要同步取消订阅<代码>弱提示::锁定
是一个同步操作。

回答正确,+1。但是,如果您明确提到通过提供合适的
D
@Angrew:Nice one,仍然可以使用
std::unique\u ptr
,您可能会做得更好,因为您找到了我问题中没有写的真正的潜在问题;)@纳瓦兹:谢谢。在我的例子中,我需要一个类型擦除删除器,但它似乎也是可能的(以一些堆分配为代价)。基本上,这是否意味着第三种类型的智能指针实际上有一个合适的位置:具有类型擦除功能的独占所有权智能指针?@AdN:我从来没有尝试过这一点,但也许您可以通过使用适当的
std::function
作为具有
unique\u ptr
的删除类型来实现这一点?假设这真的有效,那么你就完成了,独占所有权和类型擦除删除器。语法nit:“为什么X动词Y?”应该是英语中的“为什么X动词Y?”。
{Hello World} located at [0x2364c60] is being deleted.
{595.5} located at [0x2364c40] is being deleted.
{959} located at [0x2364c20] is being deleted.
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();
    }
}
struct closure_target {
    std::function<void()> closure;
    std::weak_ptr<void> sentinel;
};
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 
    }
};