如何在 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】:

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

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++)

【讨论】:

【参考方案2】:

对于 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 中使用固定大小的数组,它的大小必须在编译时知道。【参考方案3】:

如果你分配的内存是一个类,它的构造函数做一些有用的事情,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
    

【讨论】:

【参考方案4】:

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

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

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

使用来自&lt;cstring&gt;std::memset 函数

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

使用来自&lt;algorithm&gt;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

【讨论】:

应该是向量如果你添加了 p= new int[10]() 你有一个完整的列表。 @mloskot,在您使用“new”初始化数组的第一种情况下,如何通过引用传递?如果我使用int array[SIZE] =1,2,3,4,5,6,7; 表示法,我可以使用void rotateArray(int (&amp; input)[SIZE], unsigned int k); 作为我的函数声明,使用第一个约定时会是什么?有什么建议吗? 恐怕std::memset 的例子是错误的——你传递了10,它似乎期望字节数——见en.cppreference.com/w/cpp/string/byte/memset。 (我认为这很好地说明了为什么应该尽可能避免这种低级结构。) @Suma 很棒的收获!固定的。这似乎是一个十年前的错误的候选者:-) 是的,我同意你的评论。【参考方案5】:

这是一个令人惊讶的 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 请不要混淆默认初始化和值初始化:它们在标准中都有明确定义,并且是不同的初始化。【参考方案6】:

是的,有:

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 代码”。【参考方案7】:

你总是可以使用 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 是将该数组归零的最快和最简单的方法。【参考方案8】:

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

通常,我使用 memset 或循环进行原始内存动态分配,具体取决于我预计该代码区域未来的可变性。

【讨论】:

【参考方案9】:

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

对于将原始整数类型的数组专门设置为 0,memset 很好,尽管它可能会引起注意。还可以考虑calloc,尽管由于演员阵容,从 C++ 中使用有点不方便。

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

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

【讨论】:

【参考方案10】:

假设您确实想要一个数组而不是 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,我不会感到惊讶。 不。使用“值初始化”将所有成员设置为 0。

以上是关于如何在 C++ 中使用 new 运算符初始化内存?的主要内容,如果未能解决你的问题,请参考以下文章

new和delete运算符 c/c++

c++控制内存分配

C++ 内存分配(new,operator new)详解

C++ new和delete运算符简介

小白学习C++ 教程十六C++ 中的动态内存分配

处理类 new 和 delete 运算符 C++ 中的内存泄漏