我刚开始进入C++,我想养成一些好习惯。如果我刚刚用new操作符分配了一个int类型的数组,那么如何在不循环遍历它们的情况下将它们全部初始化为0呢?我应该只使用memset吗?有没有办法做到这一点?
- 如果你想拾取一个好的C++习惯,那么避免直接使用数组,而是使用向量。无论类型如何,vector都将初始化所有项,然后不需要记住调用delete[]运算符。
- @Brianege:如果我需要将数组传递给一个外部C函数,我可以给它一个向量吗?
- 你可以通过&vector[0]。
- 当然,当您将数组传递给C函数时,通常必须指定指向第一个元素的指针,&;vector[0]如@jamesdlin所说,以及在本例中由vector.size()提供的数组大小。
- 相关(要求非数组类型):stackoverflow.com/questions/7546620/…
这是一个出人意料的鲜为人知的C++特性(事实证明,没有人把这个作为答案),但是它实际上有一个默认的初始化数组的特殊语法(在技术上,它被称为标准中的"值初始化"):
注意,您必须使用空括号-例如,您不能使用(0)或任何其他表达式(这就是为什么它只对默认初始化有用)。
这是由ISO C++ 03 5.3.4(Exp.New)/15明确表示的:
A new-expression that creates an object of type T initializes that object as follows:
...
-
If the new-initializer is of the form (), the item is value-initialized (8.5);
并且不限制允许这样做的类型,而(expression-list)表单则被同一节中的其他规则显式限制,这样它就不允许数组类型。
- 虽然我同意这是鲜为人知的,但我不能(完全)同意这是非常令人惊讶的——它被添加到C++ 03中,大多数人似乎几乎被忽略了(因为这是它添加的少数新事物之一)。
- 杰瑞:我必须承认我还不知道(可能是因为当我读到标准时,它已经是C++ 03)了。也就是说,我所知道的所有实现都支持这一点,这是非常值得注意的(我想这是因为实现起来非常简单)。
- 是的,实现起来很简单。在新的C++中,所有的"值初始化"都是新的。
- 在C++ 11中,也可以使用统一的初始化:EDCOX1(4)。您还可以提供初始化时使用的值:new int[10] {1,2,3}。
- 请不要将默认初始化与值初始化混淆:它们都在标准中明确定义,并且是不同的初始化。
假设你真的想要一个数组而不是一个STD::vector,"C++方式"就是这个。
1 2 3 4 5
| #include
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使用memset,我不会感到惊讶。
- 不。使用"值初始化"将所有成员设置为0。
有许多方法可以分配一个内部类型的数组,所有这些方法都是正确的,尽管选择哪一个方法取决于…
手动初始化循环中的所有元素
1 2 3 4 5
| int* p = new int[10];
for (int i = 0; i < 10; i++)
{
p[i] = 0;
} |
使用的std::memset函数
1 2
| int* p = new int[10];
std::memset(p, 0, 10); |
使用的std::fill_n算法
1 2
| int* p = new int[10];
std::fill_n(p, 10, 0); |
使用std::vector容器
1
| std::vector<int> v(10); // elements zero'ed |
如果c++0x可用,使用初始化列表特性
1 2
| 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 |
- 应该是vector如果您添加了p=new int[10]()您有一个完整的列表。
- @卡斯滕修好了,干杯。
- @mloskot,在第一个使用"new"初始化数组的情况下,将如何进行传递引用?如果我使用int array[SIZE] ={1,2,3,4,5,6,7};符号,我可以使用void rotateArray(int (& input)[SIZE], unsigned int k);作为我的函数声明,当使用第一个约定时会是什么?有什么建议吗?
如果您正在分配的内存是一个具有可执行某些有用操作的构造函数的类,则new运算符将调用该构造函数并使对象保持初始化状态。
但是,如果您正在分配一个pod或一些没有初始化对象状态的构造函数的东西,那么您就不能在一个操作中使用新的操作符分配内存并初始化该内存。但是,您有几个选项:
1)改用堆栈变量。您可以在一个步骤中分配和默认初始化,如下所示:
1
| int vals[100] = {0}; // first element is a matter of style |
2)使用memset()。注意,如果您分配的对象不是pod,那么memsetting是一个坏主意。一个特定的例子是,如果您Memset一个类具有虚拟函数,那么您将吹走vtable并使对象处于不可用状态。
3)许多操作系统都有执行所需操作的调用——在堆上分配并初始化数据。一个windows示例是VirtualAlloc()。
4)这通常是最好的选择。完全不用自己管理记忆。您可以使用STL容器对原始内存进行任何操作,包括一次性分配和初始化所有内容:
1
| std::vector<int> myInts(100, 0); // creates a vector of 100 ints, all set to zero |
是的:
1
| std::vector<int> vec(SIZE, 0); |
使用矢量而不是动态分配的数组。好处包括不必费心明确地删除数组(当向量超出范围时,它会被删除),而且即使抛出异常,内存也会自动删除。
编辑:为了避免不想阅读下面评论的人进一步推倒选票,我应该更清楚地说,这个答案并不意味着Vector总是正确的答案。但它确实是一种比"手动"更可靠的C++方法来确保删除一个数组。
现在用C++ 11,也有STD::数组,它模拟一个固定大小的数组(VS向量能够增长)。还有std::unique_ptr,用于管理动态分配的数组(可以与此问题的其他答案中回答的初始化相结合)。其中任何一种都比手动处理数组指针更为简单。
- 这并不能真正回答被问到的问题。
- 我应该始终使用std::vector而不是动态分配的数组吗?在矢量上使用数组有什么好处,反之亦然?
- 我应该在我的问题中提到,数组必须在当前作用域之后保持。我必须使用new std::vector吗?
- John Knoller:OP问了C++的方式,我认为向量是C++的方式。当然,您是正确的,可能有一些情况仍然需要一个普通数组,并且不知道操作的情况,这可能是一个。不过,我想不会,因为这似乎是合理的,操作不知道向量。
- 数组速度稍快,您可以相当严格地控制它。哦,你必须设法释放内存。当与C接口时,有时必须传递C函数数组。在大多数情况下,矢量是你所需要的。
- @Dreamlax:如果它必须在作用域之后继续存在,我还是更喜欢使用向量而不是数组。只有我会用共享的指针包装它以确保它被删除。如果确实必须使用实数数组,则使用共享数组是另一个选项。(有关共享_ptr/shared_数组的描述,请参阅www.boost.org)。
- @维林泰斯帕姆:虽然这个解决方案不能回答我的问题,但这是我要走的道路。泰勒·麦克亨利更直接地回答了我的问题,尤其应该帮助那些无论如何都不能使用std::vector的人。
- @维林泰斯帕姆:如果你是写std:vector的人怎么办?那你怎么办?有人必须做这项工作,而不仅仅是把它扔进图书馆。
- @约翰·诺勒:正如我之前多次指出的那样,您不会使用动态分配的数组来实现std::vector。您使用::operator new来分配内存,并放置新的对象来在该内存中创建对象,这与实际不同,new[]根本不起作用(例如,它会立即在分配的所有内存中创建对象,而std::vector无法做到这一点)。
- @jerry:std::vector-sure-as-hell不使用堆栈分配或const,所以它一定是在某个级别上的动态分配。如果Dreamlax的问题的答案是new[]会自动初始化数组,那么如果有人真的这么说而不是偏转,那就太好了。
- @维尔廷帕斯姆:不,它不是C++的方式。它是Java的方式来实现它。无论上下文如何,都将EDOCX1 7随处粘贴称为"用C++编写Java代码"。
- @约翰诺勒:我非常明确地说它不使用动态分配的数组。我已经讨论了如何分配存储空间。底线是,几乎没有任何理由使用new[]--即使您是实现std::vector或类似功能的人。
- @杰瑞:好的,我接受。我从来没有用过新的,但我认为那只是我自己。
- @Andreyt:我同意总是在动态分配数组上使用向量不是一个好的说法。然而,我仍然认为使用向量在一个动态分配的数组上是C++的方式,除非使用一个动态分配的数组有一些特殊的原因,否则它肯定是首选的选择。这不是编写Java代码,而是以最小的努力和较低的错误风险完成任务。
std::fill是一种方式。使用两个迭代器和一个值填充区域。或者,for循环,(我想)将是更多的C++方式。
对于将原始整数类型的数组设置为0(具体来说),memset是可以的,尽管它可能会引起注意。也考虑EDCOX1的2度,尽管从C++中使用CAST有点不方便。
就我而言,我几乎总是使用循环。
(我不想再猜测人们的意图,但事实上,std::vector是,所有事物都是平等的,比使用new[]更好。)
您可以始终使用memset:
1 2
| int myArray[10];
memset( myArray, 0, 10 * sizeof( int )); |
- 我知道我可以使用EDCOX1,10,但是我不确定这是否是C++解决问题的方法。
- YUP,MeMSET在C++中同样适用于将线性内存设置为一个值。
- 虽然可能是真的,但我不会说这是"C++方式"。有更多的"可接受"的方式来做这件事。
- 它不是真正的"C++方式",但是原始数组也不是。
- @布兰特:也就是说,它在C或C++中都不太好用。它适用于类型为char或unsigned char的大多数值。它适用于大多数类型的值为0(至少在大多数实现中)。否则,它通常是无用的。
- 与10 * sizeof( int )相比,10 * sizeof( *myArray )更具文档记录和变更证明。
- 在任何情况下,op都有一个原始数组,memset是使该数组归零的最快和最简单的方法。
通常,对于项目的动态列表,使用std::vector。
通常,我使用memset或循环进行原始内存动态分配,这取决于我预测代码区域将来的变化程度。