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


174

我才刚刚开始使用C ++,我想养成一些好习惯。如果我刚刚intnew运算符分配了一个类型数组,如何将它们全部初始化为0而又不自己遍历它们呢?我应该使用memset吗?有没有一种“ C ++”的方式来做到这一点?


19
如果要养成良好的C ++习惯,请避免直接使用数组,而应使用vector。Vector将初始化所有项目,而与类型无关,因此您无需记住调用delete []运算符。
brianegge 2010年

@brianegge:如果我需要将数组传递给外部C函数,可以给它向量吗?
dreamlax 2010年

12
你可以通过&vector[0]
jamesdlin'2

当然,将数组传递给C函数时,通常必须指定指向第一个元素的指针,如@jamesdlin所说的&vector [0],以及在这种情况下由vector.size()提供的数组的大小。
Trebor Rude 2014年

相关(非数组类型的请求):stackoverflow.com/questions/7546620/…–
阿空加瓜

Answers:


391

这是C ++的一个鲜为人知的鲜为人知的功能(事实证明,还没有人给出这个答案),但是它实际上具有用于对数组进行值初始化的特殊语法:

new int[10]();

请注意,您必须使用空括号-例如,您不能使用(0)或其他任何内容(这就是为什么它仅对值初始化有用)的原因。

ISO C ++ 03 5.3.4 [expr.new] / 15明确允许这样做,它表示:

创建一个对象类型的new表达式T按如下方式初始化该对象:

...

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

并且不限制允许使用的类型,而该(expression-list)格式又受同一节中其他规则的明确限制,因此不允许使用数组类型。


1
虽然我同意这鲜为人知,但我不能(完全)同意这真的很令人惊讶-它是C ++ 03中添加的,大多数人似乎几乎忽略了它(因为这是为数不多的新事物之一)它确实添加了)。
杰里·科芬

2
@Jerry:我必须承认我还不知道(可能是因为当我阅读该标准时,已经是C ++ 03)。就是说,我知道的所有实现都支持此功能,这是非常了不起的(我想这是因为实现起来如此琐碎)。
Pavel Minaev'2

2
是的,实现起来很简单。至于是新的,所有的 “值初始化”在C ++ 03新
杰里棺材

34
在C ++ 11中,您也可以使用统一初始化:new int[10] {}。您还可以提供以下值进行初始化:new int[10] {1,2,3}
bames53 2013年

请不要混淆default-initialized和value-initialized:它们在标准中都有明确的定义,并且是不同的初始化。
Deduplicator

25

假设您确实确实想要一个数组而不是std :: vector,那么“ C ++方式”就是这样

#include <algorithm> 

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

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

但是请注意,实际上,这实际上仍然是一个将每个元素分配为0的循环(实际上并没有另一种方式,除非有硬件级别的支持的特殊体系结构)。


我不介意循环是否在某个函数下实现,我只是想知道我是否必须自己实现这种循环。谢谢你的提示。
dreamlax 2010年

4
您可能会感到惊讶。我曾是。在我的STL(包括GCC和Dinkumware)上,如果std :: copy检测到正在用内置类型调用它,则实际上变成了memcpy。如果std :: fill_n使用了memset,我不会感到惊讶。
Brian Neal '02

2
不。使用“值初始化”对所有成员设置为0
马丁纽约

23

分配固有类型数组的方法有很多,所有这些方法都是正确的,尽管选择哪种方法取决于...

手动初始化循环中的所有元素

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

使用std::memset功能<cstring>

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

使用std::fill_n来自的算法<algorithm>

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

使用std::vector容器

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

如果C ++ 0x可用,则使用初始化程序列表功能

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

1
应该是vector <int>如果添加了p = new int [10](),则您有完整的列表。
karsten

@mloskot,在使用“ new”初始化数组的第一种情况下,如何通过引用传递?如果使用int array[SIZE] ={1,2,3,4,5,6,7};符号,则可以使用void rotateArray(int (& input)[SIZE], unsigned int k);我的函数声明,使用第一个约定时会是什么?有什么建议吗?
阿努

1
恐怕该示例std::memset是错误的-您传递了10,似乎期望字节数-请参见en.cppreference.com/w/cpp/string/byte/memset。(我认为这很好地说明了为什么在可能的情况下应该避免这样的低级构造。)
Suma

@Suma很棒!固定。这似乎是十年错误的候选人:-)是的,我同意您的评论。
mloskot

7

如果您分配的内存是带有构造函数的类,该构造函数执行了一些有用的操作,则new运算符将调用该构造函数并使您的对象初始化。

但是,如果要分配POD或没有初始化对象状态的构造函数的东西,则不能在一个操作中分配内存并使用new运算符初始化该内存。但是,您有几种选择:

1)改用堆栈变量。您可以一步分配和默认初始化,如下所示:

int vals[100] = {0};  // first element is a matter of style

2)使用 memset()。请注意,如果您要分配的对象不是POD,则对其进行内存设置是一个坏主意。一个特定的示例是,如果您将一个具有虚函数的类设为memset,则会破坏该vtable并使对象处于无法使用的状态。

3)许多操作系统都有执行您想要的操作的调用-在堆上分配并将数据初始化为某种东西。Windows的例子是VirtualAlloc()

4)这通常是最好的选择。避免完全自己管理内存。您可以使用STL容器完成对原始内存的几乎所有处理,包括一次分配和初始化所有内存:

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

6

就在这里:

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

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

编辑:为了避免不打扰阅读以下评论的人造成进一步的低估,我应该更清楚地表明,这个答案并不表示向量始终是正确的答案。但是,与确保删除数组相比,它肯定是比“手动”更多的C ++方法。

现在使用C ++ 11,还有std :: array,它对恒定大小的数组进行建模(对可以增长的向量)。还有一个std :: unique_ptr,它管理动态分配的数组(可以与初始化结合使用,如对该问题的其他答案所述)。与手动处理指向数组的指针恕我直言相比,这些方法都是C ++方式。


11
这并不能真正回答所提出的问题。
约翰·诺勒

1
我应该始终使用std::vector而不是动态分配的数组吗?在向量上使用数组有什么好处,反之亦然?
dreamlax 2010年

1
@John Knoller:OP询问了使用C ++的方式,我想说vector是使用C ++的方式。当然,您是正确的,有些情况仍然需要纯数组,而又不知道OP的情况可能就是这种情况。我猜不会,因为OP不了解向量似乎是合理的。
villintehaspam'2

1
@villintehaspam:尽管此解决方案无法回答我的问题,但这是我要走的路。泰勒·麦克亨利(Tyler McHenry)更直接地回答了我的问题,尤其应该为无论出于何种原因而无法使用的人们提供帮助std::vector
dreamlax

2
@villintehaspam:不,这不是C ++的方法。这是Java的方法。vector无论上下文如何,随处粘贴都称为“用C ++编写Java代码”。
AnT 2010年

2

std::fill是一种方式。需要两个迭代器和一个值来填充区域。我认为,那或for循环将是更C ++的方式。

要将原始整数类型的数组专门设置为0 memset是可以的,尽管这可能会引起注意。还考虑calloc,尽管由于强制转换在C ++中使用有点不方便。

就我而言,我几乎总是使用循环。

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


1

您可以随时使用memset:

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

我知道我可以使用memset,但是我不确定这是否是解决问题的C ++方法。
dreamlax 2010年

1
这并不是真正的“ C ++方式”,但也不是原始数组。
Pete Kirkham'2

1
@gbrandt:这是说它在C或C ++中都不能很好地工作。它适用于char或unsigned char类型的大多数值。它适用于大多数类型,值为0(至少在大多数实现中)。否则,它通常是无用的。
杰里·科芬

1
10 * sizeof( *myArray )比拥有更多的文档记录和更可靠的更改10 * sizeof( int )
凯文·里德

1
无论如何,OP都有一个原始数组,内存集是将该数组归零的最快,最简单的方法。
Gregor Brandt'2

-1

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

通常,我将memset或循环用于原始内存动态分配,具体取决于我对代码区域的期望程度。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.