返回在堆中分配的实例与在堆栈中分配的实例
Returning instances allocated in the heap vs in the stack
距离我上次用c++写东西已经有很多年了。这些天,有人在一个学校的c++项目上向我寻求帮助,我对我所看到的语言的一个"特性"很感兴趣,这个特性工作得很好,但我预计它不会工作。
我记得,我可以在堆上或堆栈上创建类的实例:
int main() {
MyClass *inHeap = new MyClass();
MyClass inStack = MyClass();
}
据我所知,第一个变量 inHeap
将使编译器在main
堆栈帧中保留一些堆栈,足以容纳指针(4字节?8个字节?诸如此类),它指向位于堆中的对象的实际实例。
同时,第二个变量 inStack
将使编译器保留足够的堆栈来保存main
堆栈帧中MyClass
的完整实例。
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
的堆栈帧中?如果是这种情况,当函数createInStack
返回时,这段代码会强制将实例复制到main
的堆栈帧吗?这个复制是如何执行的,即,它只是在main
函数中自动调用复制构造函数吗?我想到的另一种可能性是编译器足够聪明,已经为
main
堆栈框架中的MyClass
实例保留了内存。这存在吗?这是某种优化来避免可能昂贵的复制吗?
作为最后一个问题,当我在堆栈中创建实例时,它的析构函数究竟在什么时候被调用?创建它的作用域何时结束?
如果你正在做这样的事情:
MyClass createInStack()
{
MyClass return_value;
return return_value;
}
int main()
{
MyClass inStack = createInStack();
}
那么是的,c
在逻辑上是在createInStack()
的堆栈上创建的,然后它的一个副本在逻辑上返回,然后在main
中复制到inStack
。
有一个常见的误解,认为按值返回是低效的,因为所有这些逻辑复制。然而,由于命名返回值优化,这在实践中并不是实际发生的。调用函数中的构造步骤只是延迟到被调用函数。它应该是这样的(伪代码):
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
,只是不创建它,但您可以非常肯定,至少命名返回值优化将避免大量复制。
还应该指出,调用new
并不是一个没有成本的操作。它可能有很大的开销,而且它肯定比在堆栈上进行本地复制使用更多的内存——当然,delete
必须由程序员处理和记住,并且也将花费超过零的时间。
所以,像往常一样,这是一个更好的设计决策,并理解正在发生的事情(以及理解"返回值优化"已经在前面的答案中涵盖)。一个非常大的类将花费很长时间来复制,但是一个只有几个值的小类在堆栈上复制可能比分配和释放它更快。
- 多个文件的内存分配错误"在抛出 'std :: bad_alloc' what (): std :: bad_alloc 的实例后终止调用" [C++]
- 我有一个对象,它将在整个程序的持续时间内实例化,但一个类成员不会,我应该动态分配它吗?
- 堆栈分配的类类型.为什么两个 ID 实例的地址相同?
- 我想知道我是否可以将一个类分配给特定的成员数据并创建该类的实例
- 是否应该使用继承来减少内存消耗的实例的内存分配?
- 使用 pybind11 包装C++分配的实例
- 如何在C++中动态分配全局字符数组中的类实例?
- 如何在C++中将类的实例完全重新分配给同一类的另一个实例(然后删除原始对象)?
- 如何将不同的成员函数指针分配给注册类的不同实例?
- 将来自类的特定实例的函数分配给变量以保持状态
- 当指针指向的对象被另一个类的实例删除时,重新分配指针
- 将成员函数分配给实例成员的属性
- 如何将内存分配给具有数组成员的类的实例
- (C++)用于实例化新对象并将其分配给指向相同对象类型的指针的 2D 向量的语法
- 删除C++中动态分配的类实例
- 不允许将结构实例分配给类实例
- 使用单一实例分配静态成员
- 如何为c++类的多个实例分配随机优先级
- 将未初始化的类实例分配给类指针会导致分段错误
- 为类的每个实例分配唯一 ID