从 unique_ptr<T[]> 初始化 shared_ptr<T>

Initialization of shared_ptr<T> from unique_ptr<T[]>

[ 问题的跟进]

我最近一直在处理指向 c-style 数组的智能指针。我最终完成了推荐的事情并改为使用指向向量的智能指针,但在那段时间里,我得到了一些建议:不要使用 shared_ptr<T> object 来管理最初由make_unique<T[]> 因为它不会调用 delete[] 而是 delete

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


此代码:

#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";
}

产生这个输出:

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

这似乎表明 unique_ptr<T[]> 的删除器已传递给 shared_ptr<T>,如我所料。


来自 C++14 标准 § 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.

如果我没看错,这意味着 shared_ptr object 从 unique_ptr 的指针和删除器构造自身。此外,我的理解(从对原始问题的回答) unique_ptr<T[]>::pointer 类型是 T*,应该可以转换为 shared_ptr<T>::pointer 的 [=28] =].所以删除器应该直接从 unique_ptr object 复制,对吧?


我的测试之所以有效,是因为它实际上并不等同于 std::make_shared<T[]> 的功能,还是语法

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

的一个很好的、异常安全的(和更清洁的)替代品
std::shared_ptr<T> mysharedArray(new T[16], [](T* ptr){delete[] ptr;});

及其同类,当我无法使用 Boost 的 shared array 并希望避免在我的代码中包含 vectorarray header 时?

是的,您的示例是有效的,原因与您陈述的完全相同。 unique_ptr::pointerint *,而您正试图将其所有权传递给 shared_ptr<int>,因此您列出的转换构造函数将参与重载决议,并将复制删除器 (std::default_delete<int[]>),因为它不是引用类型。

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

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

除了您显示的 lambda 之外,另一种写法是

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

这将导致 mySharedArray 获取与上一行相同的删除器。