基本概念

,英文是 heap,在内存管理的语境下,指的是动态分配内存的区域。这个堆跟数据结构里的堆不是一回事。这里的内存,被分配之后需要手工释放,否则,就会造成内存泄漏。

C++ 标准里一个相关概念是自由存储区,英文是 free store,特指使用 new 和 delete 来分配和释放内存的区域。一般而言,这是堆的一个子集:

new 和 delete 操作的区域是 free store.

malloc 和 free 操作的区域是 heap

但 new 和 delete 通常底层使用 malloc 和 free 来实现,所以 free store 也是 heap。

,英文是 stack,在内存管理的语境下,指的是函数调用过程中产生的本地变量和调用数据的区域。这个栈和数据结构里的栈高度相似,都满足“后进先出”(last-in-first-out 或 LIFO)。

RAII,完整的英文是 Resource Acquisition Is Initialization,是 C++ 所特有的资源管理方式。有少量其他语言,如 D、Ada 和 Rust 也采纳了 RAII,但主流的编程语言中, C++ 是唯一一个依赖 RAII 来做资源管理的

RAII 依托栈和析构函数,来对所有的资源——包括堆内存在内——进行管理。对 RAII 的使用,使得 C++ 不需要类似于 Java 那样的垃圾收集方法,也能有效地对内存进行管理。RAII 的存在,也是垃圾收集虽然理论上可以在 C++ 使用,但从来没有真正流行过的主要原因。

在堆上分配内存,有些语言可能使用 new 这样的关键字,有些语言则是在对象的构造时隐式分配,不需要特殊关键字。不管哪种情况,程序通常需要牵涉到三个可能的内存管理器的操作:

1.让内存管理器分配一个某个大小的内存块

2.让内存管理器释放一个之前分配的内存块

3.让内存管理器进行垃圾收集操作,寻找不再使用的内存块并予以释放

C++ 通常会做上面的操作 1 和 2。Java 会做上面的操作 1 和 3。而 Python 会做上面的操作 1、2、3。这是语言的特性和实现方式决定的。

RAII

C++ 支持将对象存储在栈上面。但是,在很多情况下,对象不能,或不应该,存储在栈上。比如:对象很大;对象的大小在编译时不能确定;对象是函数的返回值,但由于特殊的原因,不应使用对象的值返回。常见情况之一是,在工厂方法或其他面向对象编程的情况下,返回值类型是基类(的指针或引用)。下面的例子,是对工厂方法的简单演示:

  1. enum class shape_type {
  2. circle,
  3. triangle,
  4. rectangle,

  5. };
  6.  
  7. class shape { };
  8. class circle : public shape { };
  9. class triangle : public shape { };
  10. class rectangle : public shape { };
  11.  
  12. shape* create_shape(shape_type type)
  13. {

  14. switch (type) {
  15. case shape_type::circle:
  16. return new circle(…);
  17. case shape_type::triangle:
  18. return new triangle(…);
  19. case shape_type::rectangle:
  20. return new rectangle(…);

  21. }
  22. }

这个 create_shape 方法会返回一个 shape 对象,对象的实际类型是某个 shape 的子类,圆啊,三角形啊,矩形啊,等等。这种情况下,函数的返回值只能是指针或其变体形式。如果返回类型是 shape,实际却返回一个 circle,编译器不会报错,但结果多半是错的。这种现象叫对象切片(object slicing),是 C++ 特有的一种编码错误。这种错误不是语法错误,而是一个对象复制相关的语义错误,也算是 C++ 的一个陷阱了,大家需要小心这个问题。

那么,我们怎样才能确保,在使用 create_shape 的返回值时不会发生内存泄漏呢?

答案就在析构函数和它的栈展开行为上。我们只需要把这个返回值放到一个本地变量里,并确保其析构函数会删除该对象即可。一个简单的实现如下所示:

  1. class shape_wrapper {
  2. public:
  3. explicit shape_wrapper(
  4. shape* ptr = nullptr)
  5. : ptr_(ptr) {}
  6. ~shape_wrapper()
  7. {
  8. delete ptr_;
  9. }
  10. shape* get() const { return ptr_; }
  11. private:
  12. shape* ptr_;
  13. };
  14.  
  15. void foo()
  16. {

  17. shape_wrapper ptr_wrapper(
  18. create_shape(…));

  19. }

如果你好奇 delete 空指针会发生什么的话,那答案是,这是一个合法的空操作。

01 | 堆、栈、RAII:C++里该如何管理资源?(极客时间笔记)的更多相关文章

  1. <现代C++实战30讲>笔记 01 | 堆、栈、RAII:C++里该如何管理资源?

    1.堆(heap),动态分配的内存区域,分配之后需手工释放(new, delete, malloc, free) 这种方式需要分配内存,释放内存,因此可能会造成内存泄露,或者内存碎片的问题. 2.栈( ...

  2. NET的堆和栈04,对托管和非托管资源的垃圾回收以及内存分配

    在" .NET的堆和栈01,基本概念.值类型内存分配"中,了解了"堆"和"栈"的基本概念,以及值类型的内存分配.我们知道:当执行一个方法的时 ...

  3. java数据结构之(堆)栈

    (堆)栈概述栈是一种特殊的线性表,是操作受限的线性表栈的定义和特点•定义:限定仅在表尾进行插入或删除操作的线性表,表尾—栈顶,表头—栈底,不含元素的空表称空栈•特点:先进后出(FILO)或后进先出(L ...

  4. 堆”,"栈","堆栈","队列"以及它们的区别

    如果你学过数据结构,就一定会遇到“堆”,"栈","堆栈","队列",而最关键的是这些到底是什么意思?最关键的是即使你去面试,这些都还会问到, ...

  5. 堆,栈,内存管理, 拓展补充-Geekband

    8, 堆,栈,内存管理 栈:  local objects 在离开作用域之后就会被消除.  堆: new MyClass 一直会存在 静态对象: static local object    作用域在 ...

  6. SQL Server中的事务日志管理(5/9):完整恢复模式里的日志管理

    当一切正常时,没有必要特别留意什么是事务日志,它是如何工作的.你只要确保每个数据库都有正确的备份.当出现问题时,事务日志的理解对于采取修正操作是重要的,尤其在需要紧急恢复数据库到指定点时.这系列文章会 ...

  7. 分享一下SQLSERVER技术交流QQ群里的群共享资源

    分享一下SQLSERVER技术交流QQ群里的群共享资源 SQLSERVER技术交流QQ群已经开了一段时间了,人数已经有了100多号人, 而群里面很多SQLSERVER爱好者上传了他们宝贵的SQLSER ...

  8. 栈的压入、弹出顺序 牛客网 剑指Offer

    栈的压入.弹出顺序 牛客网 剑指Offer 题目描述 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序.假设压入栈的所有数字均不相等.例如序列1,2,3,4,5是 ...

  9. 堆&栈的理解(转)

    (摘自:http://www.cnblogs.com/likwo/archive/2010/12/20/1911026.html) C++中堆和栈的理解 内存分配方面: 堆: 操作系统有一个记录空闲内 ...

随机推荐

  1. 变量、变量作用域、常量final、变量的命名规范

    变量 变量是什么:就是可以变化的量! Java是一种强类型语言,每个变量都必须声明其类型. Java变量是程序中最基本的存储单元,其要素包括变量名,变量类型和作用域. 注意事项: 每个变量都有类型,类 ...

  2. Linux性能优化实战内存篇(五)

    一.Linux内存工作原理 1,内存映射 Linux内核给每个进程都提供了一个独立的虚拟空间,并且这个地址空间是连续的.这样,进程就可以很方便地访问内存,更确切地说是访问虚拟内存. 虚拟地址空间的内部 ...

  3. 【1024打卡】C++字符串的输出((c语言风格)

    c++字符串输出(c语言风格) 文章目录 c++字符串输出(c语言风格) 杂记 代码 杂记 今天程序设计竞赛白给了,果然还是太弱了,y总带带我TAT ┭┮﹏┭┮1024快乐 代码 c语言学习 #inc ...

  4. 最详尽教程完整介绍-Windows 的 Linux 子系统-WSL1&WSL2

    安装 WSL 1. 开启WSL 必须启用"适用于 Linux 的 Windows 子系统"可选功能并重启,然后才能在 Windows 上运行 Linux 发行版. 以管理员运行Po ...

  5. &取地址运算符

    &可以取得一个变量在内存当中的地址 Register int a; //这种变量不在内存里面,而在CPU里面,是没有地址的, 所以寄存器变量(register)不能用&来取得变量 在计 ...

  6. 【Linux】apt软件管理和远程登录

    镜像下载.域名解析.时间同步请点击 阿里云开源镜像站 1. apt 介绍 apt 是 Advanced Packaging Tool 的简称,是一款安装包管理工具.在 Ubuntu 下,可以使用 ap ...

  7. sql语言:如何查询字符串某个字符的个数?

    sql语言:如何查询字符串某个字符的个数? 这语句太精彩了! select len('05011045')-len(replace('05011045','0',''))

  8. SpringCloud项目中接入Nacos作为注册中心

    具体demo         https://blog.csdn.net/qq_33619378/article/details/95212754 <?xml version="1.0 ...

  9. Mybatis 的 Xml 映射文件中,不同的 Xml 映射文件,id 是否可以重复?

    不同的 Xml 映射文件,如果配置了 namespace,那么 id 可以重复:如果没有配 置 namespace,那么 id 不能重复: 原因就是 namespace+id 是作为 Map<S ...

  10. 什么是 Apache Kafka?

    Apache Kafka 是一个分布式发布 - 订阅消息系统.它是一个可扩展的,容错的 发布 - 订阅消息系统,它使我们能够构建分布式应用程序.这是一个 Apache 顶 级项目.Kafka 适合离线 ...