如何在 C++ 中使用 new 运算符初始化内存?
Posted
技术标签:
【中文标题】如何在 C++ 中使用 new 运算符初始化内存?【英文标题】:How to initialise memory with new operator in C++? 【发布时间】:2011-01-13 08:01:45 【问题描述】:我刚开始接触 C++,我想养成一些好习惯。如果我刚刚使用 new
运算符分配了一个 int
类型的数组,我怎样才能将它们全部初始化为 0 而无需自己循环遍历它们?我应该只使用memset
吗?有没有“C++”的方式来做到这一点?
【问题讨论】:
如果你想养成良好的 C++ 习惯,那就避免直接使用数组,而是使用向量。 Vector会初始化所有的item,不管是什么类型,然后你就不用记得调用delete[]操作符了。 @brianegge:如果我需要将数组传递给外部 C 函数,我可以只给它向量吗? 你可以传&vector[0]
。
当然,当您将数组传递给 C 函数时,您通常必须指定指向第一个元素的指针,如 @jamesdlin 所说的 &vector[0] 以及数组的大小,在此提供大小写为vector.size()。
相关(要求非数组类型):***.com/questions/7546620/…
【参考方案1】:
这是一个令人惊讶的 C++ 鲜为人知的特性(这一点可以从没有人给出这个答案的事实中得到证明),但它实际上具有用于对数组进行值初始化的特殊语法:
new int[10]();
请注意,您必须使用空括号 - 例如,您不能使用 (0)
或其他任何东西(这就是为什么这仅对值初始化有用)。
这是 ISO C++03 5.3.4[expr.new]/15 明确允许的,它说:
创建
T
类型对象的新表达式按如下方式初始化该对象:...
如果 new-initializer 的形式为()
,则该项为值初始化 (8.5);
并且不限制允许这样做的类型,而(expression-list)
形式受到同一部分中进一步规则的明确限制,因此它不允许数组类型。
【讨论】:
虽然我同意这是鲜为人知的,但我不能(完全)同意这真的非常令人惊讶——它是在 C++ 03 中添加的,大多数人似乎几乎忽略了它(因为这是它添加的为数不多的新东西之一)。 @Jerry:我必须承认我还不知道(可能是因为当我开始阅读标准时,它已经是 C++03 了)。也就是说,我知道的所有实现都支持这一点,这很了不起(我想这是因为实现起来太简单了)。 是的,实现起来非常简单。就新事物而言,all“值初始化”是 C++ 03 中的新事物。 在 C++11 中,您也可以使用统一初始化:new int[10]
。您还可以提供值进行初始化:new int[10] 1,2,3
请不要混淆默认初始化和值初始化:它们在标准中都有明确定义,并且是不同的初始化。【参考方案2】:
有很多方法可以分配一个内在类型的数组,所有这些方法都是正确的,但是选择哪一个,取决于...
手动初始化循环中的所有元素
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 可用,则使用initializer list 功能
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
【讨论】:
应该是向量int array[SIZE] =1,2,3,4,5,6,7;
表示法,我可以使用void rotateArray(int (& input)[SIZE], unsigned int k);
作为我的函数声明,使用第一个约定时会是什么?有什么建议吗?
恐怕std::memset
的例子是错误的——你传递了10,它似乎期望字节数——见en.cppreference.com/w/cpp/string/byte/memset。 (我认为这很好地说明了为什么应该尽可能避免这种低级构造。)
@Suma 很棒的收获!固定的。这似乎是十年前错误的候选者:-) 是的,我同意你的评论。【参考方案3】:
假设您确实想要一个数组而不是 std::vector,那么“C++ 方式”就是这样
#include <algorithm>
int* array = new int[n]; // Assuming "n" is a pre-existing variable
std::fill_n(array, n, 0);
但请注意,实际上这仍然只是一个将每个元素分配为 0 的循环(实际上没有其他方法可以做到这一点,除非具有硬件级支持的特殊架构)。
【讨论】:
我不介意循环是否在函数下实现,我只是想知道我是否必须自己实现这样的循环。感谢您的提示。 您可能会感到惊讶。我曾是。在我的 STL(GCC 和 Dinkumware)上,如果 std::copy 检测到它被内置类型调用,它实际上会变成一个 memcpy。如果 std::fill_n 使用 memset,我不会感到惊讶。 不。使用 'Value-Initialization' 将所有成员设置为 0。【参考方案4】:是的,有:
std::vector<int> vec(SIZE, 0);
使用向量而不是动态分配的数组。好处包括不必费心显式删除数组(当向量超出范围时将其删除),并且即使抛出异常也会自动删除内存。
编辑:为了避免那些懒得阅读下面的 cmets 的人进一步投反对票,我应该更清楚地说明这个答案并不是说 vector 总是是正确的答案.但它肯定是一种比“手动”确保删除数组更 C++ 的方式。
现在使用 C++11,还有 std::array 可以模拟一个恒定大小的数组(相对于能够增长的向量)。还有 std::unique_ptr 管理一个动态分配的数组(可以与该问题的其他答案中回答的初始化相结合)。其中任何一个都是比手动处理指向数组的指针更 C++ 的方式,恕我直言。
【讨论】:
这并不能真正回答所提出的问题。 我应该总是使用std::vector
而不是动态分配的数组吗?使用数组而不是向量有什么好处,反之亦然?
@John Knoller:OP 询问了 C++ 的方法来做到这一点,我会说 vector 是 C++ 的方法来做到这一点。当然,您是正确的,可能存在仍然需要一个普通数组并且不知道 OP 的情况的情况,这可能是一种情况。不过我猜不,因为 OP 不知道向量似乎是合理的。
@villintehaspam:虽然这个解决方案没有回答我的问题,但它是我要走的路。 Tyler McHenry 更直接地回答了我的问题,尤其是对那些不能(无论出于何种原因)使用 std::vector
的人提供帮助。
@villintehaspam:不,这不是 C++ 的方式。这是Java的方式来做到这一点。无论上下文如何,在任何地方都粘贴vector
称为“用 C++ 编写 Java 代码”。【参考方案5】:
如果你分配的内存是一个类,它的构造函数做一些有用的事情,operator new 将调用该构造函数并让你的对象保持初始化。
但是,如果您要分配 POD 或没有初始化对象状态的构造函数的东西,那么您无法在一次操作中分配内存并使用 operator new 初始化该内存。但是,您有多种选择:
改为使用堆栈变量。您可以一步分配和default-initialize,如下所示:
int vals[100] = 0; // first element is a matter of style
使用memset()
。请注意,如果您分配的对象不是 POD,则对其进行内存设置是个坏主意。一个具体的例子是,如果你 memset 一个具有虚函数的类,你会炸毁 vtable 并使你的对象处于不可用状态。
许多操作系统都有执行您想要的操作的调用 - 在堆上分配并将数据初始化为某物。 Windows 示例是VirtualAlloc()
。
这通常是最好的选择。完全避免自己管理内存。您可以使用 STL 容器来执行您对原始内存所做的任何事情,包括一举分配和初始化所有内容:
std::vector<int> myInts(100, 0); // creates a vector of 100 ints, all set to zero
【讨论】:
【参考方案6】:初始化普通动态数组的可能方法。根据您的要求选择一个。
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++)
【讨论】:
【参考方案7】:std::fill
是一种方式。采用两个迭代器和一个值来填充区域。那,或者 for 循环,将(我想)是更多的 C++ 方式。
对于将原始整数类型的数组专门设置为 0,memset
很好,尽管它可能会引起注意。还可以考虑calloc
,尽管由于演员阵容,从 C++ 中使用有点不方便。
就我而言,我几乎总是使用循环。
(我不喜欢猜测人们的意图,但std::vector
确实是,在所有条件相同的情况下,最好使用new[]
。)
【讨论】:
【参考方案8】:你总是可以使用 memset:
int myArray[10];
memset( myArray, 0, 10 * sizeof( int ));
【讨论】:
我知道我可以使用memset
,但我不确定这是否是解决问题的 C++ 方法。
这不是真正的“C++ 方式”,但也不是原始数组。
@gbrandt:也就是说它在 C 或 C++ 中都不能很好地工作。它适用于大多数类型的值是 char 或 unsigned char。它适用于大多数类型的值为 0(至少在大多数实现中)。否则一般是没用的。
10 * sizeof( *myArray )
比 10 * sizeof( int )
更有记录性和防更改性。
在任何情况下,OP 都有一个原始数组,而 memset 是将该数组归零的最快和最简单的方法。【参考方案9】:
对于 c++,使用 std::array<int/*type*/, 10/*size*/>
而不是 c 样式的数组。这适用于 c++11 标准,这是一个很好的做法。有关标准和示例,请参阅here。如果您出于某种原因想坚持使用旧的 c 样式数组,有两种可能的方法:
int *a = new int[5]();
这里括号留空,否则会报编译错误。这将初始化分配数组中的所有元素。这里如果不使用括号,它仍然会用零初始化整数值,因为 new 将调用构造函数,在本例中为 int()
。
int *a = new int[5] 0, 0, 0;
这在 c++11 标准中是允许的。在这里,您可以使用所需的任何值初始化数组元素。在这里确保您的初始化列表( 中的值)大小不应大于您的数组大小。初始化器列表大小小于数组大小是可以的。数组中的剩余值将初始化为 0。
【讨论】:
std::array<>
的大小必须在编译时知道。无法根据运行时确定的大小声明std::array<>
,因此在new[]
或std::vector<>
上使用std::array<>
并不总是可行或可能的。
@dreamlax 是的,完全同意你的看法。我们只能在 std::array 中使用固定大小的数组,它的大小必须在编译时知道。【参考方案10】:
对于项目的动态列表,通常使用std::vector
。
通常,我使用 memset 或循环进行原始内存动态分配,具体取决于我预计该代码区域未来的可变性。
【讨论】:
以上是关于如何在 C++ 中使用 new 运算符初始化内存?的主要内容,如果未能解决你的问题,请参考以下文章