转自:http://www.devbean.net/2014/02/cpp-create-object-on-heap-or-stack/
如果需要在堆上创建对象,要么使用new
运算符,要么使用malloc
系列函数。这点没有异议。
真正有异议的是下面的代码:
此时,obj
是在栈上分配的吗?
要回答这个问题,我们首先要理解这个语句是什么意思。这个语句就是代表着,在栈上创建对象吗?
其实,这行语句的含义是,使对象obj
具有“自动存储(automatic storage)”的性质。所谓“自动存储”,意思是这个对象的存储位置取决于其声明所在的上下文。
如果这个语句出现在函数内部,那么它就在栈上创建对象。
如果这个语句不是在函数内部,而是作为一个类的成员变量,则取决于这个类的对象是如何分配的。考虑下面的代码:
指针pClass
所指向的对象在堆上分配空间。因为Object obj;
语句的含义是“自动存储”,所以,pClass->obj
也是在堆上创建的。
理解了这一点,再来看下面的语句:
Object *pObj;
代表,指针pObj
是自动存储的,仅此而已,没有任何其它含义。而下面一行语句则指出,这个指针所指向的对象是在堆上面分配的。如果这两行语句出现在一个函数内部,意味着当函数结束时,pObj
会被销毁,但是它指向的对象不会。因此,为了继续使用这个对象,通常我们会在函数最后添加一个return
语句,或者使用一个传出参数。否则的话,这个在堆上创建的对象就没有指针指向它,也就是说,这个对象造成了内存泄露。
并不是说指针指向的对象都是在堆上创建的。下面的代码则使用指针指向一个在栈上创建的对象:
至此,我们解释了函数内部的变量和成员变量。还有两类变量:全局变量和static
变量。它们即不在堆上创建,也不在栈上创建。它们有自己的内存空间,是除堆和栈以外的数据区。也就是说,当Object obj
即不在函数内部,又不是类的成员变量时,这个对象会在全局数据段创建,同理适用于static
变量。对于指针Object *pObj;
,如果这个语句出现在函数内部或类的成员变量,正如我们前面所说的,这个指针是自动存储的。但是,如果这个语句是在类的外部,它就是在全局数据段创建的。虽然它指向的对象可能在堆上创建,也可能在栈上创建。
堆和栈的区别在于两点:
- 生命周期
- 性能
第一点才是我们需要着重考虑的。由于栈的特性,如果你需要一个具有比其所在的上下文更长的生命周期的变量,只能在堆上创建它。所以,我们的推荐是:只要能在栈上创建对象,就在栈上创建;否则的话,如果你不得不需要更长的生命周期,只能选择堆上创建。这是由于在栈上的对象不需要我们手动管理内存。有经验的开发人员都会对内存管理感到头疼,我们就是要避免这种情况的发生。总的来说,我们更多推荐选择在栈上创建对象。
但是,有些情况,即便你在栈上创建了对象,它还是会占用堆的空间。考虑如下代码:
对象v
是在栈上创建的。但是,STL 的vector
类其实是在堆上面存储数据的(这点可以查看源代码)。因此,只有对象v
本身是在栈上的,它所管理的数据(这些数据大多数时候都会远大于其本身的大小)还是保存在堆上。
关于第二点性能,有影响,不过一般可以忽略不计。确切的说,一般情况下你不需要考虑性能问题,除非它真的是一个问题。
首先,在堆上创建对象需要追踪内存的可用区域。这个算法是由操作系统提供,通常不会是常量时间的。当内存出现大量碎片,或者几乎用到 100% 内存时,这个过程会变得更久。与此相比,栈分配是常量时间的。其次,栈的大小是固定的,并且远小于堆的大小。所以,如果你需要分配很大的对象,或者很多很多小对象,一般而言,堆是更好的选择。如果你分配的对象大小超出栈的大小,通常会抛出一个异常。尽管很罕见,但是有时候也的确会发生。有关性能方面的问题,更多出现在嵌入式开发中:频繁地分配、释放内存可能造成碎片问题。
现代操作系统中,堆和栈都可以映射到虚拟内存中。在 32 位 Linux,我们可以把一个 2G 的数据放入堆中,而在 Mac OS 中,栈可能会限制为 65M。
总的来说,关于究竟在堆上,还是在栈上创建对象,首要考虑你所需要的生命周期。当性能真正成为瓶颈的时候,才去考虑性能的问题。堆和栈是提供给开发者的两个不同的工具,不存在一个放之四海而皆准的规则告诉你,一个对象必须放在堆中还是在栈中。选择权在开发者手中,决定权在开发者的经验中。
相关推荐
禁止创建栈对象,意味着只能在堆上创建对象。创建栈对象时会移动栈顶指针以“挪出”适当大小的空间,然后在这个空间上直接调用类的构造函数以形成一个栈对象。而当栈对象生命周期结束,如栈对象所在函数返回时,会...
在C++编程中,了解内存的管理至关重要,尤其是在处理数据结构和对象的生命周期时。本文将深入探讨C++中的堆、栈以及静态数据区,帮助理解这些内存区域的区别和使用场景。 1. 栈(Stack): 栈是编译器自动管理的...
在C++编程语言中,创建一个栈类是数据结构学习和实际应用中的一个重要知识点。栈是一种后进先出(LIFO)的数据结构,通常用于解决各种算法问题,如括号匹配、函数调用堆栈等。下面将详细介绍如何在C++中创建一个栈类...
在C++编程语言中,根据变量的存储方式和生命周期,可以将内存区域大致分为四个部分:静态存储区、栈、堆以及未初始化的数据段(BSS)。其中,静态存储区、栈与堆是程序运行时频繁使用的三个重要内存区域。本文将重点...
`new`关键字在Java、C++、C#等面向对象语言中广泛使用,它用于在堆内存中动态分配空间并初始化一个新对象。当我们使用`new`关键字时,通常会伴随着一个类的构造函数调用,以便对新对象进行初始化。 ```java // Java...
1. **对象与类**:在C++中,类是创建对象的蓝图,它定义了一组数据成员(变量)和成员函数(方法)。对象是类的实例,具有类所定义的属性和行为。创建对象时,内存会分配给对象的数据成员,并可以调用成员函数来操作...
使用类模板的好处是,你可以创建不同类型的栈,例如整数栈、字符串栈或自定义对象栈,只需指定不同的类型参数即可。 在实现这些功能时,我们还需要考虑异常处理,比如当尝试从一个空栈中弹出元素时,应该抛出一个...
在C++编程中,创建对象有两种主要方式:使用`new`关键字在堆上分配和不使用`new`在栈上分配。这两种方式有着显著的区别,理解这些差异对于编写高效、安全的代码至关重要。 首先,栈(Stack)和堆(Heap)是两种不同...
在堆上创建的对象需要程序员手动调用`delete`来释放内存,并执行析构函数。 对于包含动态分配内存或持有其他资源(如文件句柄、网络连接等)的类,析构函数通常用于释放这些资源。在堆栈上,由于析构的自动性,...
《深度探索C++对象模型》是一本专注于C++底层机制的专著,主要针对2012年的标准进行深入解析。...通过阅读本书,读者将能够更好地掌握C++这一强大工具,无论是在系统编程、游戏开发还是其他领域,都能游刃有余。
- **对象的析构**:无论是位于堆还是栈上的对象,它们都会在适当的时候被析构。理解这一点对于避免内存泄漏等问题非常重要。 - **STL容器的管理**:`vector`等STL容器在内部处理了元素的构造和析构,使得开发者无需...
通过`malloc`(C语言)或`new`(C++)等操作,程序员可以在堆中申请任意大小的内存空间。由于分配和回收的复杂性,相对于栈,堆的管理效率较低,但提供了更大的灵活性。程序员可以自由决定何时创建和释放对象,但...
2. C++:C++在C的基础上增加了类和对象的概念,对象通常在堆上分配,以实现动态的对象生命周期管理。同时,C++引入了智能指针如`shared_ptr`和`unique_ptr`,帮助开发者更好地管理堆内存,避免内存泄漏。栈上的内存...
2. **构造与析构**:在C++中,构造函数用于初始化新创建的对象,而析构函数则在对象生命周期结束时执行,通常用于释放资源。理解这两者对于有效管理内存至关重要。 3. **内存管理**:C++提供了两种主要的内存区域...
### 一个C++描述的栈类 #### 概述 本文档介绍了一个使用C++编写的通用栈类的实现。该栈类通过模板机制支持多种数据类型,并且提供了丰富的功能来管理和操作栈内的数据。栈是一种后进先出(Last In, First Out, LIFO...
在这个文件中,我们将创建栈对象,执行一些操作,并打印结果以验证我们的实现是否正确。例如: ```cpp #include #include "MyStack.h" int main() { Stack s; s.push(1); s.push(2); s.push(3); std::cout ...
在C++编程中,模版类是一种强大的工具,它允许我们创建可以处理多种数据类型的通用类。本项目中,模版类被用来实现两种常见的数据结构——顺序栈和链栈,这两种栈都遵循后进先出(LIFO)的原则。下面我们将详细探讨...
在C++中,你可以使用`new`操作符在堆上分配内存,使用`delete`操作符释放内存。堆的分配和释放过程较慢,因为它涉及到搜索合适的空闲块和更新内存管理数据结构等操作。然而,堆的优点是它提供了更大的灵活性,可以...
3. **构造与析构**:构造函数用于初始化新创建的对象,而析构函数则在对象生命周期结束时执行清理工作。理解这两个过程可以帮助我们避免资源泄漏和确保对象状态的正确性。 4. **继承与多态**:继承允许一个类(派生...