堆上的结构数组没有正确初始化
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
),这总是一个好主意,编译器会告诉你这样的事情。将警告视为错误,除非您确定它们不是错误:)
为什么这些奇怪的数字?
因为没有分配内存来支持它们。指针指向克罗姆知道什么。这个结构不能编译。
如果我不定义构造函数,数组中的A对象不会以某种方式正确构造吗?
如果没有构造函数,所有成员都将初始化为其默认值。int
和大多数Plain Old数据类型没有定义的默认值。在一个典型的实现中,它们获取恰好已经在其分配的内存块中的任何值。如果成员对象的类型没有默认构造函数,并且无法创建默认构造函数,则会得到编译器错误。
为什么我需要明确指定数组的大小,即使它将编译和链接很好没有?
不应该编译,数组的大小(未指定,对其本身来说是一个错误)和初始化列表中的元素数量不匹配,因此编译器有一个错误。
在静态版本中,编译器可以计算初始化列表中元素的个数。为什么动态版带新不能,不得不说我没有好的答案。你可能会认为这只是简单的计算初始化列表,所以有更深层次的东西在阻止它。争论和批准该标准的人要么从未考虑过以这种方式分配动态数组,要么找不到一种让它在所有情况下都能工作的好方法。同样的原因,可变长度数组仍然不在标准中。以这种方式初始化堆栈上的数组不会给我一个运行时错误(我测试了它以确保)。
"为什么我需要明确地指定数组的大小,即使它可以编译和链接很好吗?它不应该编译,...."需要说明的是:如果我将构造函数添加到A中并运行它,在delete[]语句之前,它运行得很好。只有这样,它才会崩溃,但会退出<<C[0]工作正常
这是因为你运气不好。该构造函数写入程序拥有的内存,但没有分配给c
。打印这些值是有效的,但是在那个时候应该在内存中的东西已经被覆盖了。这可能会导致您的程序迟早崩溃。这次是晚些时候。
我的怀疑,这是基于特定的猜测,因为你已经冒险进入了未定义的领域,delete[]
上的崩溃是因为
A* c = new A[]
分配A[1]
并将其分配给c
而不是编译失败。c
有一个A工作。初始化列表试图填充4,并将3写入c[0]
,并将4、5和6写入delete需要放回数据的堆控制信息。一切看起来都很好,直到delete试图使用覆盖的信息。
哦,还有这个:"如果没有构造函数,所有成员都将初始化为默认值。int型和大多数普通旧式数据类型没有定义的默认值。对于结构体,用户定义的actor似乎是可选的,因为您可以通过提供与其数据字段相对应的参数来初始化结构体。
结构体对数据封装的态度比类宽松得多,并且默认为public
访问,而类默认为private
。我从未尝试过,但我敢打赌,您可以使用相同的结构技巧来初始化类的所有公共成员。
OK。试一试。在GCC 4.8.1中工作。如果不查一下标准,我是不会笼统地这么说的。我得复制一份
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- 在函数内部的声明中初始化数组,并在外部使用它
- 为什么用结构初始化数组需要指定结构名称
- 有没有一种代码密度较低的方法来使用非默认构造函数初始化数组?
- C++使用另一个数组和新值初始化数组
- 初始化数组、"memset"或" {//value} "的最佳方法是什么?
- 在 constexpr 构造函数中初始化数组是否合法?
- 我可以初始化 const 实例,以便我可以将其用作 const 来初始化数组吗?
- 在构造函数中初始化数组
- 是否可以使用函数返回的值初始化数组
- 使用宏使用额外元素初始化数组
- 在循环中显示不同值的初始化数组
- 如何在macOS中的旧扩展clang和gcc编译器中初始化数组和向量
- 使用可变模板列表初始化数组,并放置new
- 使用函数从 Visual Studio 2017 中的 main 创建和初始化数组
- 使用 c++ 中的函数初始化数组
- 这是使用构造函数初始化数组对象的最佳方法吗?
- C++ 使用变量而不是常量表达式初始化数组
- 在初始化列表中初始化数组的更好方法
- 在可变参数模板类中初始化数组