我的C++程序有一个非常大的const数组,应该在堆上分配,但由于某种原因,它会破坏我的堆栈。它是这样的:
Foo.h
#pragma once
#include <vector>
struct Data
{
short id;
std::vector<short> points;
Data(short i, std::vector<short> p) : id(i), points(p) {}
}
class Foo
{
private:
short id;
std::vector<Data> data;
public:
Foo(short i, std::vector<data> d) : id(i), data(d) {}
};
阵列.h
#pragma once
#include "Foo.h"
const int ARRAY_LENGTH = 1058;
static const Foo *ARRAY = new Foo[ARRAY_LENGTH]
{
Learnset(1, std::vector<Data>({
Data(1, std::vector<short>({151, 489, 435, 65, 282, 355, 92})),
Data(8, std::vector<short>({286})),
Data(19, std::vector<short>({595})),
Data(26, std::vector<short>({417, 201})),
[...]
})),
[...]
}
这个数组基本上充当程序的数据库。我知道是这个数组导致了溢出,因为如果我注释掉大部分,程序运行得很好。
更奇怪的是,当我使用sizeof
进行检查时,程序报告数组有4个字节长,就像我预期的那样,所以我认为程序可能试图在将数组移动到堆之前在堆栈上构建数组,我不确定是否是我使用向量造成的。我不知道如何改正。
导致堆栈溢出的原因是vector<Data>
构造函数的初始值设定项列表将在堆栈上构建。
当初始化命名空间范围的静态变量时,编译器将发出初始化该变量的初始化例程。在这种情况下,您使用operator new[]
并提供用于初始化数组的值(我假设Learnset
应该是Foo
(。为了构造初始化数组的Foo
对象,例程必须调用Foo
的构造函数,传递您提供的参数。反过来,必须首先(在堆栈上(构造这些参数。
因此,为了初始化数组的第一个元素,编译器必须为向量(在堆栈上(创建short
的初始化器列表,构造向量(在栈上(,将其传递给Data
的构造函数,对每个Data
对象执行此操作以创建初始化器列表。然后调用向量构造函数,这会生成一个巨大的初始值设定项列表,该列表在传递给vector<Data>
的构造函数之前存在于堆栈中。
如果你在一个真正受堆栈约束的平台上,你可能需要让你的构造函数constexpr,或者避免通过初始化器列表进行初始化,因为初始化器列表会消耗与其大小成比例的堆栈空间。
但是,为什么要使用指针来动态分配数组,而不只是使用数组类型呢?如果使用std::array<Foo, ARRAY_LENGTH>
甚至Foo ARRAY[ARRAY_LENGTH]
,数组将只存在于数据段中,而不是动态分配。如果您的构造函数是constexpr,那么它甚至可能是编译时评估的。如果没有对其进行编译时评估,那么堆栈消耗仍然是一个问题。