如何在C++中使用new运算符初始化内存?

我刚刚开始接触C++,我想养成一些好习惯。如果我刚刚用new操作符分配了一个类型int的数组,我如何将它们全部初始化为0,而不需要自己循环它们?我应该只使用memset吗?有没有“C++”怎么做?

170938 次浏览

是的,有:

std::vector<int> vec(SIZE, 0);

使用向量而不是动态分配的数组。好处包括不必显式删除数组(当向量超出范围时,数组将被删除),并且即使抛出异常,内存也会自动删除。

编辑:为了避免那些不愿费心阅读下面评论的人投下更多的反对票,我应该更清楚地说明,这个答案并不是说Vector总是是正确答案。但它肯定是一种比“手动”确保删除数组更C++的方法。

现在,在C++11中,还有STD:Array,它对恒定大小的数组(能够增长的VS Vector)进行建模。还有一个STD:unique_PTR,用于管理动态分配的数组(可以与初始化相结合,这在这个问题的其他答案中有回答)。恕我直言,与手动处理指向数组的指针相比,这些方法中的任何一种都更像是C++。

假设您确实想要一个数组而不是一个STD:vector,“ C++方式”应该是这样的

#include <algorithm>


int* array = new int[n]; // Assuming "n" is a pre-existing variable


std::fill_n(array, n, 0);

但请注意,在引擎盖下,这实际上仍然只是一个将每个元素赋值为0的循环(除了具有硬件级支持的特殊体系结构外,实际上没有其他方法可以做到这一点)。

std::fill是一种方法。使用两个迭代器和一个值来填充区域。那,或者for循环,(我想)更像是C++的方式。

为了明确地将基元整数类型的数组设置为0,__abc0是很好的,尽管它可能会令人惊讶。还要考虑calloc,尽管由于强制转换,在C++中使用它有点不方便。

对我来说,我几乎总是使用循环。

(我不喜欢猜测人们的意图,但在所有条件都相同的情况下,std::vector确实比使用new[]更可取。)

对于动态项目列表,通常使用std::vector

通常,我使用memset或循环来进行原始内存动态分配,这取决于我对未来代码区域的预期。

如果要分配的内存是一个类,它的构造函数做了一些有用的事情,那么运算符new将调用该构造函数并使对象保持初始化状态。

但是如果你要分配一个豆荚或其他没有初始化对象状态的构造函数的东西,那么你就不能分配内存并在一个操作中用操作符new初始化内存。但是,您有以下几种选择:

  1. 请改用堆栈变量。您可以在一个步骤中分配和默认初始化,如下所示:

     int vals[100] = {0}; // first element is a matter of style
    
  2. 使用memset()。请注意,如果要分配的对象不是POD,则memsetting不是一个好主意。一个具体的例子是,如果你memset了一个有虚函数的类,你就会把vtable吹走,让你的对象处于无法使用的状态。

  3. 许多操作系统都有做你想做的事情的调用-在堆上分配并将数据初始化到某个地方。Windows示例为VirtualAlloc()

  4. 这通常是最好的选择。完全避免自己管理内存。您可以使用STL容器来做任何使用原始内存所做的事情,包括一次性分配和初始化所有内容:

     std::vector<int> myInts(100, 0); // creates a vector of 100 ints, all set to zero
    

你可以一直使用memset:

int myArray[10];
memset( myArray, 0, 10 * sizeof( int ));

有许多方法可以分配内部类型的数组,并且所有这些方法都是正确的,但选择哪一个取决于..

手动初始化回路中的所有元件

int* p = new int[10];
for (int i = 0; i < 10; i++)
p[i] = 0;

使用<cstring>中的std::memset函数

int* p = new int[10];
std::memset(p, 0, sizeof *p * 10);

使用<algorithm>std::fill_n算法

int* p = new int[10];
std::fill_n(p, 10, 0);

使用std::vector容器

std::vector<int> v(10); // elements zero'ed

如果C++11可用,则使用初始化器列表功能

int a[] = { 1, 2, 3 }; // 3-element static size array
vector<int> v = { 1, 2, 3 }; // 3-element array but vector is resizeable in runtime

令人惊讶的是,这是C++的一个鲜为人知的特性(事实证明,还没有人给出这个答案),但它实际上有特殊的语法来初始化数组的值:

new int[10]();

请注意,您使用空括号必须—例如,您不能使用(0)或其他任何东西(这就是为什么这只对值初始化有用)。

这是ISO C++035.3.4[Expr.New]/15明确允许的,它说:

创建ABC0__类型的对象的new-expression按如下方式初始化该对象:

...

  • 如果new-initializer是()的形式,则该项被值初始化(8.5);

并且不限制允许这样做的类型,而(expression-list)形式被同一节中的其他规则明确限制,因此它不允许数组类型。

对于C++,请使用std::array<int/*type*/, 10/*size*/>,而不要使用C样式的数组。这在C++11标准中是可用的,这是一个很好的实践。有关标准和示例,请参阅IT在这里。如果您出于某些原因而坚持使用旧的C样式数组,有两种可能的方法:

  1. int *a = new int[5](); 这里保留括号为空,否则将给出编译错误。这将初始化已分配数组中的所有元素。在这里,如果您不使用括号,它仍然会用零来初始化整数值,因为new将调用构造函数,在本例中它是__abc0。
  2. int *a = new int[5] {0, 0, 0}; 这在C++11标准中是允许的。在这里,您可以使用所需的任何值来初始化数组元素。在这里,确保初始化器列表({}中的值)大小不应大于数组大小。初始值设定项列表大小可以小于数组大小。数组中的剩余值将初始化为0。

初始化普通动态数组的可能方法。根据您的要求选择一个。

int* x = new int[5];          // gv gv gv gv gv (gv - garbage value)
int* x = new int[5]();        // 0  0  0  0  0
int* x = new int[5]{};        // 0  0  0  0  0  (Modern C++)
int* x = new int[5]{1,2,3};   // 1  2  3  0  0  (Modern C++)