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

Bru*_*eis 2 c++ heap stack allocation

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

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

int main() {
  MyClass *inHeap = new MyClass();
  MyClass inStack = MyClass();
}
Run Code Online (Sandbox Code Playgroud)

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

此外,第二个变量,inStack将使编译器保留堆栈足以MyClassmain堆栈帧中保存那里的完整实例.

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

MyClass *createInHeap() {
  return new MyClass();
}
int main() {
  MyClass* inHeap = createInHeap();
}
Run Code Online (Sandbox Code Playgroud)

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

MyClass createInStack() {
  MyClass c = MyClass();
  return c;
}
int main() {
  MyClass inStack = createInStack();
}
Run Code Online (Sandbox Code Playgroud)

到底发生了什么?

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

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

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

Vau*_*ato 8

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

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

int main() 
{
  MyClass inStack = createInStack();
}
Run Code Online (Sandbox Code Playgroud)

然后是,c在逻辑上堆栈上创建createInStack(),然后返回逻辑上它的一个副本,然后复制到inStackmain.

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

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);
}
Run Code Online (Sandbox Code Playgroud)

如您所见,没有实际的复制发生.

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