堆和栈 ========== 堆(Heap)与栈(Stack)有两层含义: - 程序内存布局场景下,堆与栈表示两种内存管理方式 - 数据结构场景下,堆与栈表示两种常用的数据结构 栈由操作系统自动分配释放 ,用于存放函数的参数值、局部变量等,其操作方式类似于数据结构中的栈。 堆由程序员分配释放,若程序员不释放,程序结束时由系统回收。 区别 --------- 管理方式 ^^^^^^^^^^^^ 栈由操作系统自动分配释放,无需我们手动控制; 堆的申请和释放工作由程序员控制,容易产生内存泄漏。 空间大小 ^^^^^^^^^^^^ 每个进程拥有的栈的大小要远远小于堆的大小。 理论上,程序员可申请的堆大小为虚拟内存的大小,进程栈的大小 64-bit 的 Windows 默认 1MB,64-bit 的 Linux 默认 10MB。 分配方式 ^^^^^^^^^^^^^^^ 堆都是动态分配的,没有静态分配的堆。栈有 2 种分配方式:静态分配和动态分配。静态分配是由操作系统完成的,比如局部变量的分配。 动态分配由 ``alloc`` 函数进行分配,但是栈的动态分配和堆是不同的,其动态分配是由操作系统进行释放,无需我们手工实现。 生长方式 ^^^^^^^^^^^^^^^ 堆的生长方向向上,内存地址由低到高。 栈的生长方向向下,内存地址由高到低。 分配效率 ^^^^^^^^^^^^^^^ 栈由操作系统自动分配,会在硬件层级对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。 堆则是由 C/C++ 提供的库函数或运算符来完成申请与管理,实现机制较为复杂,频繁的内存申请容易产生内存碎片。显然,**堆的效率比栈要低得多** 。 存放内容 ^^^^^^^^^^^^^^^ 栈:存放函数返回地址、相关参数、局部变量和寄存器内容等。 堆:一般在堆的头部用一个字节存放堆的大小,堆中的具体内容由程序员安排。 内存分区 ------------------- 在 C++ 中,内存主要分为堆、栈、全局/静态存储区和常量存储区。 - **栈** :就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区。里面的变量通常是局部变量、函数参数等。 - **堆** :就是那些由 new 分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个 new 就要对应一个 delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。 - **全局/静态存储区** :全局变量和静态变量被分配到同一块内存中,在以前的 C 语言中,全局变量又分为初始化的和未初始化的,在 C++ 里面没有这个区分了,他们共同占用同一块内存区。 - **常量存储区** :这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改。 构造函数的空间分配 ----------------------- **静态构造** :编译器为对象在栈空间中分配内存(直接移动栈顶指针,挪出适当的空间),然后在这片内存空间上调用构造函数构造一个栈对象。 :: A a; **动态构造** :在堆上申请内存;在堆内存上构造对象;指针指向该堆内存。 :: A* pa = new A(); 限制在堆上构造对象 ^^^^^^^^^^^^^^^^^^^^^ 要求不能在栈空间构造类对象,直接将构造函数声明为 private 是不行的。因为 new 表达式实际上也调用了构造函数,会报错 ``error: 'A::A()' is private`` 。 在栈空间构造对象,是由编译器分配内存空间的。当对象被使用完之后,编译器会调用析构函数来释放栈对象所占的空间。编译器管理了对象的整个生命周期。如果编译器无法调用类的析构函数(比如,类的析构函数是 private),则编译器无法释放内存。所以,编译器在为类对象分配栈空间时,会先检查类的析构函数的访问性(其实不光是析构函数,只要是非静态的函数,编译器都会进行检查)。 **如果类的析构函数是私有的,则编译器不会在栈空间上为类对象分配内存** 。 另一方面,考虑到类的继承,应将构造函数和析构函数声明为 **protected** ,然后提供一个静态函数来完成对象的构造。 此外,还要方便释放对象所占用的内存空间。delete 表达式会调用析构函数,如果析构函数不是 public,在类外无法直接访问。 .. code-block:: cpp :linenos: class A { protected: A(){} ~A(){} public: static A* create() { return new A(); } void destroy() { delete this; } }; .. code-block:: cpp :linenos: A* pa = A::create(); pa->destroy(); 限制在栈上构造对象 ^^^^^^^^^^^^^^^^^^^^^ 将 new 和 delete 运算符重载,并声明为 private。 .. code-block:: cpp :linenos: class A { public: A(){} ~A(){} private: void* operator new(size_t){} void operator delete(void*){} }; 参考资料 ----------- 1. 堆与栈的区别 https://blog.csdn.net/K346K346/article/details/80849966 2. C/C++——堆栈的讲解 https://blog.csdn.net/lovejay7/article/details/80662390 3. C++ 自由存储区是否等价于堆? https://www.cnblogs.com/QG-whz/p/5060894.html 4. 如何让类对象只在栈(堆)上分配空间? https://segmentfault.com/a/1190000009023942