unique_ptr有数组的用法吗?
std::unique_ptr
支持数组,例如:
std::unique_ptr<int[]> p(new int[10]);
但是它需要? 可能使用std::vector
或std::array
会更方便。
你觉得这个构造有什么用处吗?
有些人并不喜欢使用std::vector
,即使是分配器也是如此。 有些人需要一个dynamic大小的数组,所以std::array
已经不在了。 有些人从其他已知返回数组的代码中获取数组; 而且代码不会被重写为返回一个vector
或其他东西。
通过允许unique_ptr<T[]>
,您可以满足这些需求。
总之,你需要的时候使用unique_ptr<T[]>
。 当替代品根本不会为你工作。 这是最后的手段。
有折衷,你select符合你想要的解决scheme。 closures我的头顶上:
初始大小
-
vector
和unique_ptr<T[]>
允许在运行时指定大小 -
array
只允许在编译时指定大小
调整
-
array
和unique_ptr<T[]>
不允许resize -
vector
存储
-
vector
和unique_ptr<T[]>
将数据存储在对象之外(通常在堆上) -
array
直接将数据存储在对象中
仿形
-
array
和vector
允许复制 -
unique_ptr<T[]>
不允许复制
交换/移动
-
vector
和unique_ptr<T[]>
有O(1)次swap
和移动操作 -
array
有O(n)时间swap
和移动操作,其中n是数组中元素的数量
指针/引用/迭代器失效
-
array
确保指针,引用和迭代器不会在对象处于活动状态时失效,即使在swap()
-
unique_ptr<T[]>
没有迭代器; 指针和引用仅在对象处于活动状态时才由swap()
失效。 (交换之后,指针指向你交换的数组,所以它们在这个意义上仍然是“有效的”)。 -
vector
可以使任何重新分配的指针,引用和迭代器无效(并且提供一些保证重新分配只能在某些操作上发生)。
与概念和algorithm兼容
-
array
和vector
都是容器 -
unique_ptr<T[]>
不是一个容器
我不得不承认,这看起来像是一个重构基于策略的devise的机会。
你可能会使用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
std::vector
构造函数和std::vector::resize()
将会初始化T
– 但是如果T
是POD, new
将不会这么做。
请参阅C ++ 11中的值初始化对象和std :: vector构造函数
请注意, vector::reserve
不是这里的替代方法: 访问std :: vector :: reserve safe之后的原始指针?
C程序员可能会selectmalloc
不是calloc
。
一个std::vector
可以被复制,而unique_ptr<int[]>
允许表示数组的唯一所有权。 另一方面, std::array
需要在编译时确定大小,这在某些情况下可能是不可能的。
Scott Meyers在Effective Modern C ++中有这样的说法
数组的
std::unique_ptr
的存在应该只对你有兴趣,因为std::array
,std::vector
,std::string
实际上总是比原始数组更好的数据结构select。 关于唯一的情况,我std::unique_ptr<T[]>
当一个std::unique_ptr<T[]>
有意义的时候,当你使用一个类C的API,返回一个原始指针到你认为拥有的堆数组。
我认为Charles Salvia的答案是相关的: std::unique_ptr<T[]>
是初始化一个空数组的大小在编译时不知道的唯一方法。 Scott Meyers对使用std::unique_ptr<T[]>
动机有什么看法?
一些常见的模式可以在一些 Windows的Win32 API调用中find,其中使用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, eg // // 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... ...
相反, 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()); }
我已经使用unique_ptr<char[]>
来实现在游戏引擎中使用的预先分配的内存池。 这个想法是提供预分配的内存池,而不是dynamic分配用于返回碰撞请求结果和其他像粒子物理的东西,而不必在每一帧分配/释放内存。 对于这种需要内存池来分配有限生命周期(通常为一帧,两帧或三帧)的对象的情况,这种方式非常方便,不需要销毁逻辑(只有内存释放)。
简而言之:它是迄今为止最有效的内存。
一个std::string
带有一个指针,一个长度和一个“短string优化”缓冲区。 但是我的情况是我需要存储一个几乎总是空的string,这个string我有成千上万的结构。 在C中,我只是使用char *
,大部分时间它将是空的。 这也适用于C ++,除了char *
没有析构函数,并且不知道要删除它自己。 相比之下, std::unique_ptr<char[]>
在超出作用域时会自行删除。 一个空的std::string
占用了32个字节,但是一个空的std::unique_ptr<char[]>
占用了8个字节,也就是它的指针大小。
最大的缺点是,每次我想知道string的长度,我必须调用strlen
。
它们可能是最可能的答案,当你只能通过一个现有的API(思考窗口消息或线程相关的callback参数)捅一个单一的指针,在孵化的另一边有一定的生命期后,但是与调用代码无关:
unique_ptr<byte[]> data = get_some_data(); threadpool->post_work([](void* param) { do_a_thing(unique_ptr<byte[]>((byte*)param)); }, data.release());
我们都希望事情对我们很好。 C ++是其他时间。
- 你需要你的结构只包含一个二进制兼容性原因的指针。
- 您需要与返回分配有
new[]
内存的API进行交互 - 例如,您的公司或项目有一个通用的规则来防止使用
std::vector
,以防止不小心的程序员无意中引入副本 - 你想防止不小心的程序员在这种情况下意外地引入副本。
一般的规则是C ++容器比使用指针的滚动更受欢迎。 这是一个普遍的规则; 它有例外。 还有更多; 这些只是例子。
可以使用unique_ptr<char[]>
来表示C的性能和C ++的便利性。 考虑你需要在数百万(好吧,如果你不信任的话,数十亿)的string操作。 将它们中的每一个存储在单独的string
或vector<char>
对象中将是内存(堆)pipe理例程的灾难。 特别是如果你需要多次分配和删除不同的string。
但是,您可以分配一个缓冲区来存储多个string。 你不会喜欢char* buffer = (char*)malloc(total_size);
显而易见的原因(如果不是很明显,search“为什么使用智能ptrs”)。 你更喜欢unique_ptr<char[]> buffer(new char[total_size]);
通过类推,相同的性能和便利性考虑适用于非char
数据(考虑数百万个向量/matrix/对象)。
我遇到了一个情况,我不得不使用std::unique_ptr<bool[]>
,这是在HDF5库(高效的二进制数据存储库,在科学中使用了很多库)。 一些编译器(在我的情况下是Visual Studio 2015) 提供了对std::vector<bool>
压缩 (通过在每个字节中使用8个std::vector<bool>
),这对HDF5这样的事情来说是一场灾难,它不关心压缩。 用std::vector<bool>
,HDF5最终因为压缩而读垃圾。
猜猜是谁在救援,在std::vector
无法正常工作的情况下,我需要干净地分配一个dynamic数组? 🙂
如果你需要一个不能复制的dynamic数组对象,那么一个智能指针指向数组是一个很好的select。 例如,如果你需要一个primefacesarrays。