非常奇怪的构造函数行为

时间:2011-07-27 13:04:38

标签: c++

我有以下情况

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消失了。我想知道编译器在运行时如何知道在创建对象之前计算容器的大小。

4 个答案:

答案 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