我有以下情况
Class A
{
public:
A(/* x number of arguments */);
};
A::A(/*.....*/)
{
std::set<__int64> tmpSet;
tmpSet.insert( /*<num>*/ ); // repeat statement for some 30 K or more
tmpSet.insert( /*<num>*/ );
//-----repeat statement for some 30K or more times
}
main()
{
A obj(/*arguments */); // Run time Error : Stack over flow
}
我观察到的是,当我减少构造函数中插入的数量时,Error消失了。我想知道编译器在运行时如何知道在创建对象之前计算容器的大小。
答案 0 :(得分:2)
我的猜测是你的构造函数生成的编译代码不适合你的程序堆栈。您应该考虑使用循环交换insert语句,这会大大减少堆栈上的函数大小。另一种选择是将要插入的数据放入静态数组中,然后在构造函数中循环它以生成集合。
答案 1 :(得分:2)
std::set.insert()
返回std::pair<iterator, bool>
。编译器必须分配堆栈内存来存储返回值,因为它太大而无法放入寄存器。
一个很好的优化是重用这个堆栈内存用于下一次调用。看起来你的编译器没有这样做。
因此,构造函数需要大量的堆栈空间。在运行时,它会尝试分配它,然后失败。
解决方案:
请注意,它非常依赖于您使用的编译器。但是,目前我不知道你使用了哪种编译器。
编辑:请注意,这是纯猜测,因为我们不知道使用了哪个编译器。
答案 2 :(得分:1)
它与容器无关,发生的事情是你传递的30,000个参数会占用堆栈空间,而你正在超过一个(合理的)限制。当您在构造函数中减少对这些参数的使用时,编译器会优化掉未使用的参数。
例如。如果你将(a,b,c)传递给构造函数但从不引用那些参数变量,编译器就可以完全删除它们 - 没有必要使用从未使用过的东西。在您的情况下,这会减少使用的堆栈数量并使其工作。
答案 3 :(得分:1)
//repeat statement for some 30K or more times
嗯,出于各种原因,这可能是一个坏主意。假设不是30K的手工输入代码,请将数据粘贴在文件或资源中并循环读取。
std::set
的实际'数据'是堆分配而不是堆栈分配,所以这不太可能是问题。
但是,堆栈溢出的原因可能就是这个。
对于许多编译器(以及许多调用约定),参数在堆栈上传递。
根据需要将insert
的参数压入堆栈。但是,编译器会在函数结束时将所有弹出内容优化为单个“大弹出”。不幸的是,你的编译器还没有意识到推送的参数数量非常大......
这是编译器所做的伪代码(忽略插入的结果)
优化前:
PUSH arg1
CALL tmpset.insert
ADD 4 to SP // pop arg1
PUSH arg2
CALL tmpset.insert
ADD 4 to SP // pop arg2
PUSH arg3
CALL tmpset.insert
ADD 4 to SP // pop arg3
优化后:
PUSH arg1
CALL tmpset.insert
PUSH arg2
CALL tmpset.insert
PUSH arg3
CALL tmpset.insert
ADD 12 to SP // pop arg1,arg2,arg3