C++ 使用阵列是否有任何独特的_ptr用途?

C++ 使用阵列是否有任何独特的_ptr用途?,c++,c++11,smart-pointers,unique-ptr,C++,C++11,Smart Pointers,Unique Ptr,std::unique_ptr支持阵列,例如: std::unique_ptr<int[]> p(new int[10]); std::unique_ptr p(新整数[10]); 但这是必要的吗?使用std::vector或std::array可能更方便 你觉得这个构造有用吗?可以复制std::vector,而unique\u ptr可以表示数组的唯一所有权std::array要求在编译时确定大小,这在某些情况下可能是不可能的。有些人甚至使用分配器也没有使用std::vecto

std::unique_ptr
支持阵列,例如:

std::unique_ptr<int[]> p(new int[10]);
std::unique_ptr p(新整数[10]);
但这是必要的吗?使用
std::vector
std::array
可能更方便


你觉得这个构造有用吗?

可以复制
std::vector
,而
unique\u ptr
可以表示数组的唯一所有权<另一方面,code>std::array要求在编译时确定大小,这在某些情况下可能是不可能的。

有些人甚至使用分配器也没有使用
std::vector
的奢侈。有些人需要一个动态大小的数组,所以
std::array
已经过时了。有些人从其他已知返回数组的代码中获取数组;代码不会被重写为返回一个
向量

通过允许
unique\u ptr
,您可以满足这些需求


简言之,您可以在需要时使用
unique\ptr
。当替代方案根本不适合你的时候。这是一个万不得已的工具。

我使用了
unique\u ptr
来实现游戏引擎中使用的预分配内存池。其思想是提供预先分配的内存池,而不是动态分配,用于返回碰撞请求结果和其他内容,如粒子物理,而无需在每个帧分配/释放内存。对于这种需要内存池来分配生命周期有限(通常为1、2或3帧)且不需要销毁逻辑(仅内存释放)的对象的场景,这非常方便

存在权衡,您可以选择符合您需要的解决方案。不经意间:

初始尺寸

  • vector
    unique\ptr
    允许在运行时指定大小
  • array
    仅允许在编译时指定大小
调整大小

  • array
    unique\ptr
    不允许调整大小
  • vector
储藏

  • vector
    unique\ptr
    将数据存储在对象外部(通常在堆上)
  • array
    将数据直接存储在对象中
临摹

  • 数组
    向量
    允许复制
  • 唯一\u ptr
    不允许复制
交换/移动

  • vector
    unique_ptr
    具有O(1)时间
    交换和移动操作
  • array
    有O(n)个time
    swap
    和move操作,其中n是数组中的元素数
指针/引用/迭代器无效

  • array
    确保指针、引用和迭代器在对象处于活动状态时永远不会失效,即使在
    swap()
  • unique\u ptr
    没有迭代器;指针和引用仅在对象处于活动状态时通过
    swap()
    无效。(交换后,指针指向您交换的数组,因此在这个意义上它们仍然是“有效的”。)
  • vector
    可能会使任何重新分配上的指针、引用和迭代器无效(并提供某些保证,即重新分配只能发生在某些操作上)
与概念和算法的兼容性

  • array
    vector
    都是容器
  • 唯一\u ptr
    不是容器

我必须承认,这似乎是一个利用基于策略的设计进行重构的机会。

当您只需在现有API(想想窗口消息或线程相关回调参数)中插入一个指针时,它们可能是最正确的答案,这些API在被“捕获”后具有一定的生命周期在图案填充的另一侧,但与调用代码无关:

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

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

我们都希望事情对我们有利。C++是其他时候。

< p> >如果你不想支付数组的运行时成本,你可以使用<代码> UnQuyGPPT。
std::vector<char> vec(1000000); // allocates AND value-initializes 1000000 chars

std::unique_ptr<char[]> p(new char[1000000]); // allocates storage for 1000000 chars
标准向量向量向量(1000000);//分配并初始化1000000个字符 std::unique_ptr p(新字符[1000000]);//为1000000个字符分配存储
std::vector
构造函数和
std::vector::resize()
将值初始化
T
——但是如果
T
是POD,则
new
不会这样做

请注意,
vector::reserve
不是此处的替代方案:


这与C程序员可能选择
malloc
而不是
calloc

的原因是一样的,在WindowsWin32 API调用中可以找到一种常见的模式,在这种模式中,使用
std::unique_ptr
很方便,例如,在调用某些Win32 API时,您不知道输出缓冲区应该有多大(这将在该缓冲区内写入一些数据):

//由调用方动态分配的缓冲区,并由某些Win32 API函数填充。
//(将在下面的“while”循环中进行分配。)
std::唯一的ptr缓冲区;
//缓冲区长度,以字节为单位。
//使用期望在第一次API调用时成功的初始长度进行初始化。
UINT32缓冲区长度=/*…*/;
LONG returnCode=错误\u缓冲区不足\u;
while(returnCode==错误\u缓冲区不足)
{
//分配指定长度的缓冲区
复位(字节[bufferLength]);
//        
//或者,在C++14中,可以使用make_unique()代替,例如。
//
//buffer=std::使_唯一(bufferLength);
//
//
//调用一些Win32 API。
//
//如果缓冲区的大小(存储在“bufferLength”中)不是
// 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...
...
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());
}
class ALargeAndComplicatedClassWithLotsOfDependencies;

class MyClass {
   ...
private:
   std::unique_ptr<ALargeAndComplicatedClassWithLotsOfDependencies[]> m_InternalArray;
};
#include "myclass.h"
#include "ALargeAndComplicatedClassWithLotsOfDependencies.h"

// MyClass implementation goes here
       Dynamic     |   Runtime static   |         Static
Stack std::vector      unique_ptr<T[]>          std::array
Heap  std::vector      unique_ptr<T[]>     unique_ptr<std::array>