2015-10-19 45 views
2

我想我知道如何在C++处理内存管理,但这让我感到困惑:阵列上堆结构不正确初始化

考虑下面的代码:

struct A { 
    int i; 
}; 

int main(int argc, char* argv[]) { 
    A a{ 5 }; //Constructs an A object on the stack 
    A* b = new A{ 7 }; //Constructs an A object on the heap and stores a pointer to it in b 
    A* c = new A[] { //Construct an array of A objects on the heap and stores a pointer to it in c 
     { 3 }, 
     { 4 }, 
     { 5 }, 
     { 6 } 
    }; 
    std::cout << "a: " << a.i << "\n"; //Prints 'a: 5' 
    std::cout << "b: " << b->i << "\n"; //Prints 'b: 7' 
    std::cout << "c: " << c[0].i << "; " << c[1].i << "; " << c[2].i << "; " << c[3].i << "\n"; 
    //Prints 'c: -33686019; -1414812757; -1414812757; -1414812757' 

    delete b; 
    delete[] c; 
    return 0; 
} 

我不明白为什么c的最后打印输出了那些奇怪的数字。如果我添加一个构造函数一个像这样:

struct A { 
    A(int i) : i{i} {} 
    int i; 
}; 

然后最后打印出的输出变为:

'c: 3; 4; 5; 6' 

理所应当。但现在delete[] c;会给我一个运行时错误(不是一个例外),说MyGame.exe has triggered a breakpoint.(我在VS2013工作)。

此外,如果我将行A* c = new A[] {更改为A* c = new A[4] {,则错误消失,并且所有内容都按预期工作。

所以我的问题是: 为什么奇怪的数字?如果我没有定义构造函数,数组中的A对象会不会得到正确的构造? 为什么我需要明确指定数组的大小,即使它会编译和链接就好了没有?以这种方式初始化堆栈上的数组不会给我一个运行时错误(我测试过它是肯定的)。

+1

如果你要在堆上分配数组,你需要指定数组的大小。你分配的是一个指针(例如'A *')而不是一个数组。从它读取调用未定义的行为。 –

+1

[这里](http://ideone.com/hZA0iM)它甚至不能编译! –

+0

奇怪的数字是因为你的编译器有点坏。 g ++ 5.1表示“错误:预期的初级表达式'之前']'标记'和'错误:'A [1]'的初始化程序太多了。 VS 2015说“错误C3078:数组大小必须在新表达式中指定”。 VS2013对该代码做了什么不清楚。 – molbdnilo

回答

1

Why the weird numbers?

因为没有分配内存来支持它们。指针指向Crom知道什么。该结构不应该编译。

Won't the A objects in the array get properly constructed somehow if I don't define a constructor?

没有构造函数,所有的成员将被初始化为默认值。 int的和大多数普通旧数据类型没有定义的默认值。在一个典型的实现中,他们得到已经在分配的内存块中发生的任何值。如果成员对象的类型不是默认构造函数,并且无法创建,那么会出现编译器错误。

And why do I need to specify the array size explicitly even though it will compile and link just fine without?

它不被编译,阵列的大小(未指定的和本身就是一个错误),并在初始化列表中元素的数量,所以编译器之间的失配有一个错误。此时链接器不参与。

Initializing arrays on the stack this way does not give me a runtime error (I tested it to be sure).

在静态版本中,编译器可以计算初始化列表中元素的数量。为什么新的动态版不能,要说我没有好的答案。你会认为这只是计算初始化列表的一个简单的步骤,所以有更深的阻止它的东西。那些辩论然后批准标准的人从未考虑过以这种方式分配一个动态数组,或者找不到一种在所有情况下都能正常工作的好方法。相同原因可变长度数组仍然不在标准中。

"And why do I need to specify the array size explicitly even though it will compile and link just fine without? It shouldn't compile, ...." To be clear: If I add the constructor to A and run it, it runs just fine up until the delete[] statement. Only then it crashes but cout << c[0] works as 'expected'

这是因为你不走运。该构造函数正在写入您的程序拥有的内存,但未分配给c。打印这些值是有效的,但是在这一点上应该记忆的内容已被覆盖。这可能会导致您的程序迟早会崩溃。这次晚了。

我怀疑,这是基于特定的猜测,因为你已经远远冒险进入不确定的领域,都在delete[]崩溃是因为

A* c = new A[] 

分配A[1]并将其分配给c,而不是未能编译。 c有一个A可以使用。初始化程序列表尝试填入4,并将3写入c[0],4,5和6以覆盖堆控制信息,删除需要将数据放回。所有看起来都不错,直到删除尝试使用覆盖的信息。

Oh and this:"Without a constructor all of the members will be initialized to their defaults. int's and most Plain Old Datatypes have no defined default value.". For structs a user defined ctor seems optional because you can initialize a struct by providing arguments corresponding to its data fields.

一个结构具有朝向数据封装不是类和默认值一个更宽容的态度来public访问其中一类默认为private。我从来没有尝试过,但我敢打赌,你可以使用相同的结构技巧来初始化一个类的所有公共成员。

好的。刚刚尝试过。适用于GCC 4.8.1。一般情况下,如果不在标准中查找,就不会提出这种说法。必须得到它的副本。

+0

“为什么我需要明确地指定数组的大小,即使它会编译并且链接正好没有?它不应该编译......”要清楚: 如果我将构造函数添加到A并运行它,它运行得很好,直到delete []语句。只有它崩溃,但cout << c [0]按照'预期'运作。 – Jupiter

+0

Oh and this:“没有构造函数,所有的成员都会被初始化为默认值,int和大多数Plain Old Datatypes都没有定义默认值。”对于结构体来说,用户定义的ctor似乎是可选的,因为您可以通过提供与其数据字段相对应的参数来初始化结构体。 – Jupiter

+0

@BitJunky无法在评论中正确回答这些问题。将它们添加到答案中。 – user4581301

5

这是一个错误:

A* c = new A[] { {3}, {4}, {5}, {6} }; 

你必须把尺寸[]内。使用new无法从初始值设定项列表中推导出数组维数。

4放在这里让你的代码对我来说是正确的。

您的编译器显然有一个“扩展”,将new A[]视为new A[1]

如果以标准模式编译(使用gcc或clang,-std=c++14 -pedantic),这总是一个好主意,编译器会告诉你这样的事情。将警告视为错误,除非您确定它们没有错误:)

+0

要迂腐,所有你需要的是'-std = C++ 11'和一个gcc或clang的版本来识别这个选项。你不需要移动到C++ 14,你不需要'-pedantic'。 Clang(正确地)在'new A [] {...}'上,因为根据标准,在方括号内应该有一个表达式。 gcc不会拒绝'new A []',因为它将它解释为'new A [1]'。然而,它确实妨碍了三个额外的初始化器。 –