初始化shared_ptr< T> from unique_ptr< T []> [英] Initialization of shared_ptr<T> from unique_ptr<T[]>

查看:226
本文介绍了初始化shared_ptr< T> from unique_ptr< T []>的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

[关注问题]



我最近在处理一些有关c型数组的智能指针。我最终绕过做推荐的事情,并使用智能指针代替向量,但在这段时间,我有一点建议:不要使用 shared_ptr< T> 对象来管理最初用 make_unique< T []> 制作的数组,因为它不会调用 delete [] 而是 delete



这对我来说似乎不合逻辑,我检查了 Coliru 和标准:






此代码:

  #include< iostream> 
#include< memory>

int main()
{
std :: cout< start!\\\
;
auto customArrayAllocator = [](unsigned int num){
std :: cout< custom array allocator\\\
;
return new int [num];
};

std :: cout<< allocator constructed \\\
;

auto customArrayDeleter = [](int * ptr){
std :: cout< custom array deleter\ n;
delete [] ptr;
};

std :: cout<< deleter constructed \\\
;

std :: unique_ptr< int [],decltype(customArrayDeleter)>
myUnique(customArrayAllocator(4),customArrayDeleter);

std :: cout<< unique_ptr constructed \\\
;

std :: shared_ptr< int>
myShared = std :: move(myUnique);

std :: cout<< shared_ptr constructed \\\
;
}

产生此输出:

 开始! 
构造的分配器
构造的删除器
自定义数组分配器
unique_ptr构造
shared_ptr构造
自定义数组删除器

这似乎表明将 unique_ptr 的删除器传递给 shared_ptr< T>






C ++ 14标准§20.8.2.2.1 pg。

template shared_ptr(unique_ptr& r);



备注:此构造函数不应参与重载解析,除非unique_ptr ::指针
可转换为T *。

效果:当D不是引用类型时,等价于shared_ptr(r.release(),r.get_deleter());
否则shared_ptr(r.release(),ref(r.get_deleter )。

异常安全:如果抛出异常,构造函数不起作用。



$ b b

如果我正在读,那意味着 shared_ptr 对象从 unique_ptr 。此外,我的理解(从原始问题的答案) ::指针类型 unique_ptr< T []> T * ,应该可以转换为 shared_ptr< T> :: pointer ' T * 。所以删除者应该只是从 unique_ptr 对象复制,对吧?






我的测试只工作,因为它实际上不等同于 std :: make_shared< ; T []> ,或者是语法

  std :: shared_ptr& mySharedArray = std :: make_unique< T []>(16); 

一个很好的,异常安全(和更干净)的替代

  std :: shared_ptr< T> mysharedArray(new T [16],[](T * ptr){delete [] ptr;}); 

和它的ilk,当我无法使用Boost的共享数组,希望避免包含向量 code> array 我的代码头?

解决方案

你说的很正常的原因。 unique_ptr :: pointer 是 int * ,并且您尝试将所有权传递给 shared_ptr< int> ,因此您列出的转换构造函数将参与重载解析,并创建删除器的副本( std :: default_delete< int [ ]> ),因为它不是引用类型。



因此下面是有效的,将调用 delete [ ] 当 shared_ptr 引用计数变为零时

$ <$ p $ b <$ p
<$ p <$ c> $ c> std :: shared_ptr< T> mySharedArray = std :: make_unique< T []>(16);

另一种方式来写这个,除了你显示的lambda,

  std :: shared_ptr< T> mySharedArray(new T [16],std :: default_delete< int []>()); 

这将导致 mySharedArray deleter作为上一行。


[Followup to this question]

I've been dealing a little bit with smart pointers to c-style arrays recently. I ultimately wound up doing the recommended thing and using smart pointers to vectors instead, but during that period, I got a bit of advice: don't use a shared_ptr<T> object to manage an array initially made with make_unique<T[]> because it won't call delete[] but rather delete.

This didn't seem logical to me, and I checked both Coliru and the Standard:


This code:

#include <iostream>
#include <memory>

int main()
{
    std::cout << "start!\n";
    auto customArrayAllocator = [](unsigned int num){
        std::cout << "custom array allocator\n";
        return new int[num];
    };

    std::cout << "allocator constructed\n";

    auto customArrayDeleter = [](int *ptr){
        std::cout << "custom array deleter\n";
        delete[] ptr;
    };

    std::cout << "deleter constructed\n";

    std::unique_ptr<int[], decltype(customArrayDeleter)>
        myUnique(customArrayAllocator(4), customArrayDeleter);

    std::cout << "unique_ptr constructed\n";

    std::shared_ptr<int>
        myShared = std::move(myUnique);

    std::cout << "shared_ptr constructed\n";
}

produces this output:

start!
allocator constructed
deleter constructed
custom array allocator
unique_ptr constructed
shared_ptr constructed
custom array deleter

Which seems to indicate that the unique_ptr<T[]>'s deleter is passed to the shared_ptr<T>, as I expected.


From the C++14 Standard § 20.8.2.2.1 pg. 571 of doc, 585 of pdf

template shared_ptr(unique_ptr&& r);
Remark: This constructor shall not participate in overload resolution unless unique_ptr::pointer is convertible to T*.
Effects: Equivalent to shared_ptr(r.release(), r.get_deleter()) when D is not a reference type, otherwise shared_ptr(r.release(), ref(r.get_deleter())).
Exception safety: If an exception is thrown, the constructor has no effect.

If I'm reading that right, it means that a shared_ptr object constructs itself from both the pointer and the deleter of a unique_ptr. Furthermore, it's my understanding (from the answer to the original question) that the ::pointer type of unique_ptr<T[]> is T*, which should be convertible to shared_ptr<T>::pointer's T*. So the deleter should just be copied right from the unique_ptr object, right?


Did my test only work because it's not actually equivalent to the function of std::make_shared<T[]>, or is the syntax

std::shared_ptr<T> mySharedArray = std::make_unique<T[]>(16);

a good, exception safe (and cleaner) alternative to

std::shared_ptr<T> mysharedArray(new T[16], [](T* ptr){delete[] ptr;});

and its ilk, when I am unable to use Boost's shared array and desire to avoid including either the vector or the array header with my code?

解决方案

Yes, your example is valid for the very reasons you've stated. unique_ptr::pointer is int *, and you're trying to pass ownership of that to a shared_ptr<int>, so the converting constructor you've listed will participate in overload resolution, and will make a copy of the deleter (std::default_delete<int[]>) because it's not a reference type.

So the following is valid, and will call delete[] when the shared_ptr reference count goes to zero

std::shared_ptr<T> mySharedArray = std::make_unique<T[]>(16);

Another way to write this, other than the lambda you've shown, is

std::shared_ptr<T> mySharedArray(new T[16], std::default_delete<int[]>());

which will result in mySharedArray acquiring the same deleter as the previous line.

这篇关于初始化shared_ptr&lt; T&gt; from unique_ptr&lt; T []&gt;的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆