Answers:
有些人甚至没有使用std::vector
分配器,也不享受使用的奢侈。有些人需要一个动态大小的数组,所以std::array
就不用了。有些人从其他已知返回数组的代码中获取数组。而且该代码不会被重写以返回a vector
或其他内容。
通过允许unique_ptr<T[]>
,您可以满足这些需求。
简而言之,您unique_ptr<T[]>
在需要时使用。当替代品根本无法为您服务时。这是不得已的工具。
vector
”。您可以争论这些条件是否合理,但您不能否认它们的存在。
std::vector
人们无法使用std::unique_ptr
。
unique_ptr
,但是确实存在那些类型的项目。
需要权衡取舍,然后选择与所需内容匹配的解决方案。从我的头顶上:
初始尺寸
vector
并unique_ptr<T[]>
允许在运行时指定大小array
仅允许在编译时指定大小调整大小
array
并且unique_ptr<T[]>
不允许调整大小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
可能会使任何重新分配上的指针,引用和迭代器无效(并提供一些保证,即重新分配只能在某些操作上进行)。与概念和算法的兼容性
array
而且vector
都是容器unique_ptr<T[]>
不是容器我必须承认,这似乎是基于策略设计进行某些重构的机会。
vector
。然后,您要增加大小或容量,以vector
强制重新分配。然后,该迭代器,指针或引用不再指向的元素vector
。这就是我们所说的“无效”。不会发生此问题array
,因为没有“重新分配”。实际上,我只是注意到了一个细节,并对其进行了编辑以适合需要。
unique_ptr<T[]>
没有重新分配而导致无效。但是,当然,当数组超出范围时,指向特定元素的指针仍将无效。
T[]
,大小(或等效信息)必须挂在某个地方operator delete[]
才能正确破坏数组的元素。如果程序员可以访问它,那就太好了。
您可能使用a的原因之一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
-但new
不会做,如果T
是一个POD。
请参见C ++ 11中的值初始化对象和std :: vector构造函数
请注意,这vector::reserve
不是替代方法:在std :: vector :: reserve之后访问原始指针是否安全?
这是同样的原因,C程序员可能选择malloc
了calloc
。
std::unique_ptr
没有提供任何边界检查违背了不少的std::vector
实现。
std::vector
标准要求它检查中的界限.at()
。我想您的意思是某些实现也具有可以检入的调试模式.operator[]
,但是我认为这对于编写良好的可移植代码没有用。
一个std::vector
可以复制的周围,同时unique_ptr<int[]>
允许表达阵列的唯一所有权。std::array
另一方面,它要求在编译时确定大小,这在某些情况下可能是不可能的。
unique_ptr
而不是来阻止这种情况shared_ptr
。我想念什么吗?
unique_ptr
不仅仅是防止意外滥用。与相比,它更小且开销更低shared_ptr
。关键是,尽管在类中包含防止“滥用”的语义是很好的,但这并不是使用特定类型的唯一原因。并且,vector
作为数组存储unique_ptr<T[]>
,如果没有其他理由,它的大小要大得多。
vector
过unique_ptr<T[]>
如有可能,而不是只是说,“你不能复制”,因此挑选unique_ptr<T[]>
时,你不想拷贝。阻止某人做错事不一定是上课的最重要原因。
std::vector
比a有更多的开销std::unique_ptr
-它使用〜3指针而不是〜1。 std::unique_ptr
阻止复制构造,但启用移动构造,如果从语义上讲,您正在使用的数据只能被移动而不能被复制,则会感染class
包含该数据的数据。对无效数据进行操作实际上会使您的容器类变得更糟,并且“只是不使用它”并不能消除所有的过失。必须将您的每个实例std::vector
放到您手动禁用的类中,move
这令人头疼。std::unique_ptr<std::array>
有一个size
。
Scott Meyers在有效的现代C ++中有此说法
的存在
std::unique_ptr
对数组应该只有智慧感兴趣的是你的,因为std::array
,std::vector
,std::string
几乎总是更好的数据结构的选择,要比原阵列。关于唯一可以std::unique_ptr<T[]>
理解的情况是,当您使用类似C的API时,该API返回指向您拥有所有权的堆数组的原始指针。
我认为,查尔斯·索尔维亚(Charles Salvia)的回答很重要:这std::unique_ptr<T[]>
是初始化一个空数组的唯一方法,该数组的大小在编译时未知。Scott Meyers对于这种使用动机会说些什么std::unique_ptr<T[]>
?
vector
stackoverflow.com/a/24852984/2436175,它还是一个效率更高的可能原因。
在某些 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...
...
std::vector<char>
在这些情况下使用。
我遇到了不得不使用std::unique_ptr<bool[]>
HDF5库(用于高效二进制数据存储的库,在科学上使用了很多)的情况。某些编译器(在我的情况下为std::vector<bool>
Visual Studio 2015)提供了压缩(通过在每个字节中使用8个布尔值),这对于诸如HDF5之类的东西来说是灾难性的,它并不关心这种压缩。使用std::vector<bool>
,HDF5最终由于该压缩而读取垃圾。
猜猜谁在那儿进行救援,以防万一,std::vector
但是我需要干净地分配一个动态数组?:-)
简而言之:它是迄今为止内存效率最高的。
A std::string
带有一个指针,一个长度和一个“短字符串优化”缓冲区。但是我的情况是我需要以几乎成千上万的结构存储几乎总是空的字符串。在C语言中,我只会使用char *
,并且大多数时候它都为null。它也适用于C ++,除了a char *
没有析构函数,并且不知道删除自身。相反,std::unique_ptr<char[]>
当a 超出范围时将删除自身。空值std::string
占用32个字节,但空值std::unique_ptr<char[]>
占用8个字节,即其指针的大小。
最大的缺点是,每当我想知道字符串的长度时,都必须调用strlen
它。
为了回答人们认为您“必须”使用的问题,vector
而不是unique_ptr
在Device中分配内存时必须在GPU上进行CUDA编程的情况,您必须使用指针数组(带有cudaMalloc
)。然后,在主机中检索此数据时,必须再次使用指针,并且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<ALargeAndComplicatedClassWithLotsOfDependencies>
或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兼容。
PS除了上述考虑之外,还有一个所有权:std::array
并且std::vector
具有值语义(对值的复制和传递具有本机支持),而unique_ptr<T[]>
只能移动(强制单个所有权)。在不同情况下,这两种方法都可能有用。相反,普通静态数组(int[N]
)和普通动态数组(new int[10]
)两者都不提供,因此应尽可能避免-在绝大多数情况下应该是可以的。如果这还不够的话,普通动态数组也无法查询其大小-内存损坏和安全漏洞的额外机会。
当您只能通过现有的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<char[]>
可以在想要C的性能和C ++的便利性的地方使用。考虑您需要对数百万个字符串(如果您还不信任,则为数十亿个)进行运算。在一个单独的存储它们中的每string
或vector<char>
物体将是存储器(堆)管理例程灾难。特别是如果您需要多次分配和删除不同的字符串。
但是,您可以分配一个缓冲区来存储那么多字符串。char* buffer = (char*)malloc(total_size);
由于明显的原因,您不会想要(如果不明显,则搜索“为什么使用智能ptrs”)。你想unique_ptr<char[]> buffer(new char[total_size]);
以此类推,相同的性能和便利性考量适用于非char
数据(考虑数百万个向量/矩阵/对象)。
vector<char>
吗?我想答案是因为创建缓冲区时它们将被初始化为零,而如果使用则不会unique_ptr<char[]>
。但是,您的答案中缺少这个关键要素。
new[]
std::vector
,例如,防止粗心的程序员意外引入副本有一个普遍的规则,那就是C ++容器比带指针的滚动容器更可取。这是一条一般规则;它有例外。还有更多; 这些只是示例。
如果您需要一个不可复制构造的动态对象数组,那么一个指向数组的智能指针是您的最佳选择。例如,如果您需要一个原子数组怎么办。
std::shared_ptr<T[]>
,但应该存在,如果有人愿意写提案,那么C ++ 14中应该存在。同时,总有boost::shared_array
。