C++ 堆栈与堆分配

2021-12-24 00:00:00 memory-management c++

我想知道什么时候应该在 C++ 的堆栈上分配一个类?我在 Java 方面有很强的背景,在 Java 中,所有类都使用 new 关键字在堆上分配.在 C++ 中,我可以在堆栈和堆分配之间进行选择,但是现在引入了智能指针,使用 std::unique_ptr 分配所有不转移所有权的东西更有意义.

I was wondering when should I allocate a class on the stack in C++? I have a strong background in Java and in Java all classes are allocated on the heap, using the new keyword. In C++ I can pick between stack and heap allocation but now that smart pointers are introduced, it makes more sense to allocate everything that doesn't transfer ownership with std::unique_ptr.

我真的想不出任何需要或更好地使用堆栈分配的情况.也许是为了在嵌入式系统中进行某种优化?

I can't really think of any cases, where it would be necessary or better to use stack allocation. Maybe for some kind of optimisation in embedded systems?

推荐答案

每当函数作用域 - 或控制块的作用域,例如 for, 时,使用自动(堆栈)分配whileif 等在函数内部 - 非常适合对象所需的生命周期.这样,如果对象拥有/控制任何资源,例如动态分配的内存、文件句柄等 - 它们将在析构函数调用期间随着该范围的剩余而被释放.(不是在垃圾收集器结束的某个不可预测的后期时间).

Use automatic (stack) allocation whenever the function scope - or the scope of a control block such as a for, while, if etc. inside the function - is a good match for the lifetime the object needs. That way, if the object owns/controls any resources, such as dynamically allocated memory, file handles etc. - they will be freed during the destructor call as that scope is left. (Not at some unpredictable later time when a garbage collector winds up).

仅在有明确需要时才使用 new,例如:

Only use new if there's a clear need, such as:

  • 需要对象的寿命比函数作用域长,

  • needing the object to live longer than the function scope,

将所有权移交给其他代码

to hand over ownership to some other code

拥有一个指向基类的指针容器,然后您可以对其进行多态处理(即使用虚拟分派到派生类函数实现),或

to have a container of pointers to base classes that you can then process polymorphically (i.e. using virtual dispatch to derived-class function implementations), or

特别大的分配会占用大部分堆栈(您的操作系统/进程将协商"一个限制,通常在 1-8+ 兆字节范围内)

an especially large allocation that would eat up much of the stack (your OS/process will have "negotiated" a limit, usually in the 1-8+ megabyte range)

  • 如果这是您使用动态分配的唯一原因,并且您确实希望将对象的生命周期绑定到函数中的作用域,则应该使用本地 std::unique_ptr<> 来管理动态内存,并确保无论你如何离开范围都会释放它:通过returnthrowbreak等.. (您也可以在 class/struct 中使用 std::unique_ptr<> 数据成员来管理对象拥有的任何内存.)
  • if this is the only reason you're using dynamic allocation, and you do want the lifetime of the object tied to a scope in your function, you should use a local std::unique_ptr<> to manage the dynamic memory, and ensure it is released no matter how you leave the scope: by return, throw, break etc.. (You may also use a std::unique_ptr<> data member in a class/struct to manage any memory the object owns.)

Mathieu Van Nevel 在下面评论了 C++11 移动语义――相关性是,如果堆栈上有一个小的管理对象控制大量动态分配的(堆)内存,移动语义会提供额外的保证和罚款- 粒度控制管理对象何时将其资源移交给其他代码拥有的另一个管理对象(通常是调用者,但可能是其他一些容器/对象寄存器).这种切换可以避免堆上的数据被复制/复制,即使是暂时的.此外,省略和返回值优化通常允许名义上自动/堆栈托管的变量直接在它们最终被分配/返回的内存中构造,而不是稍后复制到那里.

Mathieu Van Nevel comments below about C++11 move semantics - the relevance is that if you have a small management object on the stack that controls a large amount of dynamically allocated (heap) memory, move semantics grant extra assurances and fine-grained control of when the management object hands over its resources to another management object owned by other code (often the caller, but potentially some other container/register of objects). This handover can avoid data on the heap being copied/duplicated even momentarily. Additionally, elision and return-value-optimisation often allow nominally automatic/stack-hosted variables to be constructed directly in some memory they're eventually being assigned/returned-to, rather than copied there later.

相关文章