Std::unique_ptr支持数组,例如:

std::unique_ptr<int[]> p(new int[10]);

但这是必要的吗?可能使用std::vector或std::array更方便。

你觉得这个结构有什么用处吗?


std::vector可以被复制,而unique_ptr<int[]>允许表示数组的唯一所有权。另一方面,Std::array要求在编译时确定大小,这在某些情况下可能是不可能的。

有些人无法奢侈地使用std::vector,即使是使用分配器。有些人需要一个动态大小的数组,所以std::array已经失效。有些人从已知返回数组的代码中获取数组;这段代码不会被重写为返回一个向量或其他东西。

通过允许unique_ptr<T[]>,您可以满足这些需求。

简而言之,您可以在需要时使用unique_ptr<T[]>。当其他选择都不适合你的时候。这是最后的手段。

I have used unique_ptr<char[]> to implement a preallocated memory pools used in a game engine. The idea is to provide preallocated memory pools used instead of dynamic allocations for returning collision requests results and other stuff like particle physics without having to allocate / free memory at each frame. It's pretty convenient for this kind of scenarios where you need memory pools to allocate objects with limited life time (typically one, 2 or 3 frames) that do not require destruction logic (only memory deallocation).

这里有权衡,您可以选择与您想要的匹配的解决方案。我能想到的是:

初始大小

vector和unique_ptr<T[]>允许在运行时指定大小 数组只允许在编译时指定大小

调整

array和unique_ptr<T[]>不允许调整大小 向量是

存储

vector和unique_ptr<T[]>将数据存储在对象之外(通常在堆上) 数组将数据直接存储在对象中

复制

数组和向量允许复制 unique_ptr<T[]>不允许复制

交换/移动

vector和unique_ptr<T[]>有O(1)次交换和移动操作 数组有O(n)次交换和移动操作,其中n是数组中元素的数量

指针/引用/迭代器失效

array ensures pointers, references and iterators will never be invalidated while the object is live, even on swap() unique_ptr<T[]> has no iterators; pointers and references are only invalidated by swap() while the object is live. (After swapping, pointers point into to the array that you swapped with, so they're still "valid" in that sense.) vector may invalidate pointers, references and iterators on any reallocation (and provides some guarantees that reallocation can only happen on certain operations).

概念和算法的兼容性

array和vector都是容器 unique_ptr<T[]>不是容器

我不得不承认,对于基于策略的设计来说,这似乎是一个重构的机会。

当你只能通过一个现有的API(窗口消息或线程相关的回调参数)插入一个指针时,它们可能是正确的答案,这些指针在被“捕捉”到另一边后具有一定的生命周期,但与调用代码无关:

unique_ptr<byte[]> data = get_some_data();

threadpool->post_work([](void* param) { do_a_thing(unique_ptr<byte[]>((byte*)param)); },
                      data.release());

我们都希望事情对自己有利。c++是其他时候用的。

您可能使用unique_ptr的一个原因是,如果您不想支付初始化数组值的运行时成本。

std::vector<char> vec(1000000); // allocates AND value-initializes 1000000 chars

std::unique_ptr<char[]> p(new char[1000000]); // allocates storage for 1000000 chars

// C++20 version:
auto p = std::make_unique_for_overwrite<char[]>(1000000);

std::vector构造函数和std::vector::resize()将对t进行值初始化,但new和std::make_unique_for_overwrite将默认初始化它们,这对于PODs来说意味着什么都不做。

参见c++ 11中的值初始化对象和std::vector构造函数

注意,vector::reserve在这里不是一个替代方案:在std::vector::reserve之后访问原始指针是安全的吗?

这和C程序员选择malloc而不是calloc的原因是一样的。

在一些Windows Win32 API调用中可以找到一个常见的模式,其中使用std::unique_ptr<T[]>可以派上用场,例如,当你调用一些Win32 API(将在该缓冲区中写入一些数据)时,不知道输出缓冲区应该有多大:

// Buffer dynamically allocated by the caller, and filled by some Win32 API function.
// (Allocation will be made inside the 'while' loop below.)
std::unique_ptr<BYTE[]> buffer;

// Buffer length, in bytes.
// Initialize with some initial length that you expect to succeed at the first API call.
UINT32 bufferLength = /* ... */;

LONG returnCode = ERROR_INSUFFICIENT_BUFFER;
while (returnCode == ERROR_INSUFFICIENT_BUFFER)
{
    // Allocate buffer of specified length
    buffer.reset( BYTE[bufferLength] );
    //        
    // Or, in C++14, could use make_unique() instead, e.g.
    //
    // buffer = std::make_unique<BYTE[]>(bufferLength);
    //

    //
    // Call some Win32 API.
    //
    // If the size of the buffer (stored in 'bufferLength') is not big enough,
    // the API will return ERROR_INSUFFICIENT_BUFFER, and the required size
    // in the [in, out] parameter 'bufferLength'.
    // In that case, there will be another try in the next loop iteration
    // (with the allocation of a bigger buffer).
    //
    // Else, we'll exit the while loop body, and there will be either a failure
    // different from ERROR_INSUFFICIENT_BUFFER, or the call will be successful
    // and the required information will be available in the buffer.
    //
    returnCode = ::SomeApiCall(inParam1, inParam2, inParam3, 
                               &bufferLength, // size of output buffer
                               buffer.get(),  // output buffer pointer
                               &outParam1, &outParam2);
}

if (Failed(returnCode))
{
    // Handle failure, or throw exception, etc.
    ...
}

// All right!
// Do some processing with the returned information...
...

出于二进制兼容性的考虑,您需要结构只包含一个指针。 你需要使用一个API来返回用new[]分配的内存 例如,您的公司或项目有一个禁止使用std::vector的一般规则,以防止粗心的程序员不小心引入副本 您希望防止粗心的程序员在这种情况下意外地引入副本。

有一个普遍的规则,c++容器比使用指针滚动自己的容器更受欢迎。这是一个普遍规律;它有例外。有更多的;这些只是例子。

Scott Meyers在《Effective Modern c++》中这样说

对于数组来说,是否存在std::unique_ptr应该只对你的智力感兴趣,因为std::array, Std::vector, Std::string实际上总是比原始数组更好的数据结构选择。关于我能想到的唯一情况,当std::unique_ptr<T[]>有意义时,当你使用一个类似c的API,返回一个原始指针指向你假定拥有的堆数组时。

我认为Charles Salvia的答案是相关的:std::unique_ptr<T[]>是初始化一个在编译时不知道大小的空数组的唯一方法。对于使用std::unique_ptr<T[]>的动机,Scott Meyers会说些什么呢?

与std::vector和std::array相反,std::unique_ptr可以拥有一个NULL指针。 这在使用期望数组或NULL的C api时非常方便:

void legacy_func(const int *array_or_null);

void some_func() {    
    std::unique_ptr<int[]> ptr;
    if (some_condition) {
        ptr.reset(new int[10]);
    }

    legacy_func(ptr.get());
}

简而言之:它是迄今为止最节省内存的。

A std::string comes with a pointer, a length, and a "short-string-optimization" buffer. But my situation is I need to store a string that is almost always empty, in a structure that I have hundreds of thousands of. In C, I would just use char *, and it would be null most of the time. Which works for C++, too, except that a char * has no destructor, and doesn't know to delete itself. By contrast, a std::unique_ptr<char[]> will delete itself when it goes out of scope. An empty std::string takes up 32 bytes, but an empty std::unique_ptr<char[]> takes up 8 bytes, that is, exactly the size of its pointer.

最大的缺点是,每次我想知道字符串的长度,我必须调用strlen。

unique_ptr<char[]>可以用在你想要C的性能和c++的便利性的地方。假设您需要操作数百万(好吧,如果您还不相信,则需要操作数十亿)字符串。将它们分别存储在单独的string或vector<char>对象中对于内存(堆)管理例程来说是一场灾难。特别是当您需要多次分配和删除不同的字符串时。

但是,您可以为存储这么多字符串分配一个缓冲区。你不会喜欢char* buffer = (char*)malloc(total_size);出于显而易见的原因(如果不明显,搜索“为什么使用智能ptrs”)。unique_ptr<char[]> buffer(new char[total_size]);

通过类比,同样的性能和便利性考虑也适用于非字符数据(考虑数百万个向量/矩阵/对象)。

我遇到了一个情况,我必须使用std::unique_ptr<bool[]>,它位于HDF5库(用于高效二进制数据存储的库,在科学中使用很多)中。一些编译器(在我的例子中是Visual Studio 2015)提供std::vector<bool>的压缩(通过在每个字节中使用8个bool),这对于HDF5之类的东西来说是一个灾难,它不关心这种压缩。对于std::vector<bool>, HDF5最终会因为压缩而读取垃圾。

猜猜谁在那里进行救援,在std::vector不起作用的情况下,我需要干净地分配一个动态数组?: -)

如果您需要一个不可复制构造的对象的动态数组,那么可以使用一个指向数组的智能指针。例如,如果您需要一个原子数组怎么办?

为了回答人们认为你“必须”使用vector而不是unique_ptr,我在GPU上的CUDA编程中有一个案例,当你在Device中分配内存时,你必须使用一个指针数组(使用cudaMalloc)。 然后,当在Host中检索该数据时,必须再次寻找指针,unique_ptr可以很容易地处理指针。 将double*转换为vector<double>的额外成本是不必要的,并且会导致性能损失。

允许和使用std::unique_ptr<T[]>的另一个原因是,到目前为止还没有在响应中提到:它允许向前声明数组元素类型。

当您希望最小化头文件中的链式#include语句(以优化构建性能)时,这非常有用。

例如:

myclass.h:

class ALargeAndComplicatedClassWithLotsOfDependencies;

class MyClass {
   ...
private:
   std::unique_ptr<ALargeAndComplicatedClassWithLotsOfDependencies[]> m_InternalArray;
};

myclass.cpp:

#include "myclass.h"
#include "ALargeAndComplicatedClassWithLotsOfDependencies.h"

// MyClass implementation goes here

使用上面的代码结构,任何人都可以#include " MyClass .h"并使用MyClass,而不必包括MyClass::m_InternalArray所要求的内部实现依赖。

如果m_InternalArray被声明为std::array< alargeandcomplatedclasswithlotsofdependencies >,或者std::vector<…> -结果将尝试使用不完整的类型,这是一个编译时错误。

我对公认答案的精神再怎么反对也不为过。“最后的手段”?远非如此!

在我看来,与C语言和其他类似语言相比,c++最强大的特性之一是能够表达约束,以便在编译时检查它们,并防止意外误用。因此,在设计结构时,要问问自己它应该允许哪些操作。应该禁止所有其他用途,最好能够静态地(在编译时)实现这些限制,以免误用导致编译失败。

因此,当需要一个数组时,以下问题的答案指定了它的行为: 1. 它的大小是a)在运行时动态的,还是b)静态的,但只在运行时知道,还是c)静态的,在编译时知道? 2. 数组是否可以分配到堆栈上?

根据这些答案,我认为这是这种数组的最佳数据结构:

       Dynamic     |   Runtime static   |         Static
Stack std::vector      unique_ptr<T[]>          std::array
Heap  std::vector      unique_ptr<T[]>     unique_ptr<std::array>

是的,我认为unique_ptr<std::array>也应该被考虑,这两个都不是最后的工具。想想什么最适合你的算法。

所有这些都通过指向数据数组的原始指针(vector.data() / array.data() / uniquePtr.get())与普通C api兼容。

P. S. Apart from the above considerations, there's also one of ownership: std::array and std::vector have value semantics (have native support for copying and passing by value), while unique_ptr<T[]> can only be moved (enforces single ownership). Either can be useful in different scenarios. On the contrary, plain static arrays (int[N]) and plain dynamic arrays (new int[10]) offer neither and thus should be avoided if possible - which should be possible in the vast majority of cases. If that wasn't enough, plain dynamic arrays also offer no way to query their size - extra opportunity for memory corruptions and security holes.

医生:这是穷人的性病。

让我们把std::unique_ptr<T[]>看作一个容器。实际上,由于缺少size字段,不能直接作为容器使用,它在标准库中可用的容器的“参数空间”中占据了一个点,而这个点不被其他合适的容器共享——即使在混合中添加Boost也不行。

如果你看看我对广泛可用的类向量/连续容器的比较,并寻找与std::unique_ptr相同的特性:

堆上的分配 容量在编译时不固定 在建造后不能改变容量(没有完全清理集装箱)

你会发现除了std::dynarray,没有其他容器提供所有这些;但它实际上不在标准库中——它本应被纳入c++ 14,但最终被拒绝了。

我不仅仅是在猜测。甚至在《SO》中,人们也偶尔这样描述事物;参见@KerrekSB 2013年对这个问题的回答。