[英]Array of structs on heap not properly initialized
我以为我知道如何用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
的最后打印输出会打印那些怪异的数字。 如果我像这样向A添加构造函数:
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对象是否会以某种方式正确构造? 为何即使不编译也可以链接时,为什么仍需要显式指定数组大小? 用这种方法在堆栈上初始化数组不会给我一个运行时错误(为了确定我已经对其进行了测试)。
这是一个错误:
A* c = new A[] { {3}, {4}, {5}, {6} };
您必须将尺寸放在[]
。 使用new
,无法从初始化列表中推导出数组维。
在此处输入4
可以使您的代码对我来说正常工作。
您的编译器显然具有将new A[]
视为new A[1]
的“扩展名”。
如果您以标准模式(使用gcc或clang -std=c++14 -pedantic
)进行编译(这总是一个好主意),则编译器会告诉您类似情况。 除非您真的确定警告不是错误,否则将警告视为错误:)
为什么数字怪异?
因为没有分配内存来支持它们。 指针指向Crom知道什么。 该结构不应该编译。
如果不定义构造函数,数组中的A对象是否会以某种方式正确构造?
没有构造函数,所有成员将被初始化为其默认值。 int
和大多数Plain Old Datatypes没有定义默认值。 在典型的实现中,它们将获得恰好在其分配的内存块中的任何值。 如果成员对象的类型不是默认构造函数且无法创建,则您会收到编译器错误。
为何即使不编译也可以链接时,为什么仍需要显式指定数组大小?
它不应该编译,数组大小(未指定和自身错误)与初始化列表中的元素数量不匹配,因此编译器存在错误。 此时不涉及链接器。
用这种方法在堆栈上初始化数组不会给我一个运行时错误(为了确定我已经对其进行了测试)。
在静态版本中,编译器可以计算初始化列表中的元素数量。 为什么不能使用新的动态版本,我必须说我没有好的答案。 您可能认为只需要对初始化列表进行计数就可以了,因此还有一些更深的层次可以防止它的出现。 争论并随后批准该标准的人们从未考虑过以这种方式分配动态数组,或者找不到一种在所有情况下都能使其正常工作的好方法。 同样的原因,可变长度数组仍然不在标准中。
“并且为什么我需要显式指定数组大小,即使它可以编译和链接也没问题呢?它不应该编译,...。”要明确:如果我将构造函数添加到A并运行它,它将运行直到删除[]语句为止。 只有这样,它才会崩溃,但是cout << c [0]可以按“预期”的方式工作
这是因为您很不幸。 该构造函数正在写入程序拥有的内存,但没有分配给c
。 打印这些值是可行的,但是那时应该存储在内存中的内容已被覆盖。 这可能会导致您的程序迟早崩溃。 这次是晚了。
我的怀疑是基于特定的猜测,因为您已经冒险进入undefined领域,因为delete[]
崩溃是因为
A* c = new A[]
分配A[1]
并将其分配给c
而不是编译失败。 c
有一个A可使用。 初始化程序列表尝试填充4,然后将3写入c[0]
以及将删除需要放回数据的堆控制信息上的4,5和6。 在删除尝试使用该覆盖的信息之前,一切看起来都很不错。
哦,这:“没有构造函数,所有成员将被初始化为其默认值。int和大多数Plain Old Datatypes没有定义的默认值。” 对于结构,用户定义的ctor似乎是可选的,因为您可以通过提供对应于其数据字段的参数来初始化结构。
与类相比,结构对数据封装的态度要宽松得多,并且默认为public
访问,而类默认为private
。 我从未尝试过,但是我敢打赌,您可以使用相同的结构技巧来初始化类的所有公共成员。
好。 刚刚尝试过。 在GCC 4.8.1中工作。 一般情况下,如果不在标准中查找它,就不会提出该主张。 得到了它的副本。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.