返回在堆中分配的实例与堆栈中的实例

时间:2013-04-28 17:21:00

标签: c++ stack heap allocation

自从我上次使用C ++做一些事情以来已经很多年了。这些天,有人问我在C ++的学校项目上提供一些帮助,我对我所见过的语言的“功能”很感兴趣,这种功能很好,但我预计它不起作用。

我记得,我可以在堆上或堆栈上创建类的实例:

int main() {
  MyClass *inHeap = new MyClass();
  MyClass inStack = MyClass();
}

据我所知,第一个变量inHeap将使编译器在main堆栈帧中保留一些堆栈足以容纳指针(4 bytes?8 bytes?类似的东西),它指向生活在堆中的对象的实际实例。

此外,第二个变量inStack将使编译器保留堆栈足以在MyClass堆栈中保存main的完整实例帧。

现在,问我的问题。假设我有一个应该返回MyClass实例的函数。起初,我认为它只能返回堆中的实例:

MyClass *createInHeap() {
  return new MyClass();
}
int main() {
  MyClass* inHeap = createInHeap();
}

但我所看到的是以下内容:

MyClass createInStack() {
  MyClass c = MyClass();
  return c;
}
int main() {
  MyClass inStack = createInStack();
}

这到底发生了什么?

  • MyClass的堆栈帧中保留createInStack实例的内存?如果是这种情况,当函数main返回时,此代码是否会强制实例复制createInStack的堆栈帧?如何执行此副本,即它是否只是在main函数中为我自动调用复制构造函数?

  • 我想到的另一种可能性是编译器足够聪明,已经为MyClass堆栈帧中的main实例保留了内存。这存在吗?这是某种优化以避免制作可能昂贵的副本吗?

作为最后一个问题,当我在堆栈中创建一个实例时,究竟是什么时候调用它的析构函数?创建它的范围何时结束?

2 个答案:

答案 0 :(得分:8)

如果您正在做这样的事情:

MyClass createInStack() 
{
  MyClass return_value;
  return return_value;
}

int main() 
{
  MyClass inStack = createInStack();
}

然后是,ccreateInStack()的堆栈上进行逻辑创建,然后逻辑上返回它的副本,然后将其复制到inStack中的main

有一种常见的误解是,由于所有这些逻辑复制,按值返回效率低下。但是,由于命名的返回值优化,这不是实际中实际发生的情况。调用函数中的构造步骤仅延迟到被调用函数。它会是这样的(伪代码):

void createInStack(MyClass &return_value)
{
  return_value.MyClass(); // construct return_value (not actually valid syntax)
}

int main()
{
  MyClass inStack; // except don't call the constructor
  createInStack(inStack);
}

正如您所看到的,没有实际的复制。

此外,编译器可以进行其他优化。它甚至可能决定永远不会使用inStack而只是不创建它,但你可以非常肯定,至少命名的返回值优化将避免大量复制。

答案 1 :(得分:1)

还应该指出,调用new并不是一种无成本的操作。它可能会产生很大的开销,并且它肯定比在堆栈上制作本地副本使用更多内存 - 当然,delete必须由程序员处理和记住,并且还需要超过零时间。

通常,这是一个更好的设计决策,并了解正在发生的事情(以及理解前面答案中已经涵盖的“返回值优化”)。一个非常大的类需要很长时间才能复制,但是只有一些值的小类,在堆栈上复制可能比分配和释放它更快。