看来在 C++20 中,我们为智能指针提供了一些额外的实用函数,包括:
template<class T> unique_ptr<T> make_unique_for_overwrite();
template<class T> unique_ptr<T> make_unique_for_overwrite(size_t n);
std::make_shared
与 std::shared_ptr
相同。为什么现有的功能没有:
template<class T, class... Args> unique_ptr<T> make_unique(Args&&... args); // with empty Args
template<class T> unique_ptr<T> make_unique(size_t n);
够了吗?现有的不是使用对象的默认构造函数吗?
注意:在这些函数的早期提案中,名称为
make_unique_default_init()
。
这些新功能不同:
make_XYZ
:始终初始化指向的值(“显式初始化”,请参阅标准中的§class.expl.init)。make_XYZ_for_overwrite
:执行指向值的“默认初始化”(参见§dcl.init,标准中的第7段);在典型的机器上,这意味着对于非类、非数组类型“实际上没有初始化”。 (是的,这个术语有点令人困惑;请阅读链接中的段落。)
new int
对于唯一/共享指针,您只能通过包装现有指针来实现此目的,如下所示:
std::unique_ptr<int[]>(new int[n])
现在我们有一个包装函数。注意:请参阅相关的 ISO C++ WG21
提案 以及 这个 SO 答案
allocate_shared
、
make_shared
和make_unique
都通过执行与new T(args...)
等效的操作来初始化底层对象。在零参数的情况下,这会减少到 new T()
- 也就是说,它执行 值初始化。在许多情况下,值初始化(包括像 int
和 char
这样的标量类型、它们的数组以及它们的聚合)执行 零初始化 - 也就是说,这是为了将 a 归零而进行的实际工作。一堆数据。
也许您想要这个并且这对您的申请很重要,也许您不需要。从 P1020R1 开始,论文介绍了最初名为 make_unique_default_init
、
make_shared_default_init
和 allocate_shared_default_init
的函数(在 C++20 的全国投票评论过程中,这些函数从 meow_default_init
重命名为 meow_for_overwrite
):
内置类型的数组(例如 unsigned char
或
)在分配后立即由用户整体初始化的情况并不罕见。在这些情况下,由double
、allocate_shared
和make_shared
执行的值初始化是多余的并且会损害性能,因此需要一种选择默认初始化的方法。 也就是说,如果您编写如下代码:make_unique
auto buffer = std::make_unique<char[]>(100);
read_data_into(buffer.get());
由
make_unique
执行的值初始化会将这 100 个字节清零,这是完全没有必要的,因为无论如何你都会立即覆盖它。
新的meow_for_overwrite
函数改为执行
默认初始化,因为所使用的内存无论如何都会立即被覆盖(因此得名) - 这就是说相当于执行new T
(没有任何括号或大括号)。在我之前提到的那些情况下(例如 int
和 char
、它们的数组以及它们的聚合),默认初始化不执行初始化,这节省了时间。
对于具有用户提供的默认构造函数的类类型,值初始化和默认初始化之间没有区别:两者都只是调用默认构造函数。但对于许多其他类型,可能存在很大差异。
make_unique
使用
calloc()
而 make_unique_for_overwrite
使用 malloc()
它存在的意义在于 malloc 通常比 calloc 更快。