为什么总是在堆栈上调用构造函数?

时间:2017-10-26 20:31:01

标签: c++

出于某种原因,我决定永远不要在我的程序中使用动态内存分配。这意味着我的程序中的所有变量都是静态的,并且从不使用“new”构造函数。但是下面的代码崩溃了,并产生了一个堆栈溢出异常:

VeryLargeObject x; // Global variable -> static memory 

void ResetTheObject() 
{
   x = VeryLargeObject(); 
}

显然,我想要做的就是给x一个默认值VeryLargeObject,它是一个包含许多不同变量的结构,它们有自己不同复杂性的构造函数(所以有很多初始化工作要做)。但是这里语言/编译器已经决定在复制之前应该在堆栈上发生这种情况,并且由于VeryLargeObject对于堆栈而言太大,我的程序崩溃了。

但是我找到了解决这个问题的方法:

VeryLargeObject x; 

void ResetTheObject() 
{
   new (&x) VeryLargeObject();  
}

我之前从未听说过这个,但它确实完全符合我的要求。这是一个“新的位置”。它在指针提供的已分配(或简单静态)内存上调用构造函数。

我的问题,因为我有解决方案,是一个咆哮:为什么这不是第一个代码的默认行为?如果没有这么简单的做法(即没有“新”这个词与它有关),为什么呢?另外,为什么它会将指针发回给我,即使我刚刚提供它?我认为C ++是一种很棒的语言,但这看起来很丑陋而且没有经过深思熟虑。

1 个答案:

答案 0 :(得分:1)

首先,启用优化可能会通过第一种语法获得您想要的结果。没有它,这就是你要求编译器做的事情:

  1. 创建VeryLargeObject类型的临时对象。
  2. 将其分配到名为x
  3. 的全局变量中

    由于临时对象需要存储,编译器会在堆栈上分配它们。编译器正在做的是字面上,你要求编译器做什么。

    如果启用了优化,编译器可以了解序列是什么并保存副本。这要求编译器可以向自己肯定地证明x的旧值不会以任何方式妨碍它。由于您承认初始化非常复杂,如果编译器无法执行此操作,您可以原谅它。

    您有两种选择。你可以创建一个就地初始化函数并调用它而不是构造函数,或者你可以使用placement new,就像你一样。

    当您使用它时,放置new的危险在于它会替换x的旧值而不会对其进行适当的破坏。它只是假设x未初始化。如果您可以使用它,那么请继续使用它。就其本身而言,编译器不允许这样做。