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

10

[回答 this 问题的跟进]

最近我有一些涉及到智能指针管理c风格数组的经验。最终,我采用了推荐的方法,使用智能指针管理向量,但在那段时间里,我得到了一个建议:不要使用 shared_ptr<T> 对象来管理最初使用 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 文档第571页,PDF第585页

template shared_ptr(unique_ptr&& r);
备注:除非unique_ptr :: pointer可转换为T *,否则此构造函数不参与重载决议。
效果:当D不是引用类型时,等效于shared_ptr(r.release(),r.get_deleter()),否则等效于shared_ptr(r.release(),ref(r.get_deleter()))。
异常安全性:如果抛出异常,则构造函数无效。

如果我理解正确的话,这意味着一个shared_ptr对象从unique_ptr的指针和删除器中构造自身。此外,根据原始问题的答案,我了解到unique_ptr的::pointer类型是T*,应该可以转换为shared_ptr::pointer的T*。因此,删除器应该直接从unique_ptr对象中复制,对吗?

我做的测试只是因为它实际上与 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头文件时,该怎么办?

1
请注意,虽然保证数组被正确删除,但在将unique_ptr<Derived[]>转换为shared_ptr<Derived> -> shared_ptr<Base>时存在潜在的安全问题,请参阅http://stackoverflow.com/q/32483375/981959。 - Jonathan Wakely
1个回答

7
是的,您的示例是有效的,原因如您所述。unique_ptr::pointer 是 int *,而您正在尝试将其所有权传递给 shared_ptr,因此您列出的转换构造函数将参与重载决议,并复制删除器(std::default_delete),因为它不是引用类型。
因此,以下内容是有效的,并且在 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获取与前一行相同的删除器。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接