基本概念

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

enum class shape_type {
circle,
triangle,
rectangle,

}; class shape { … };
class circle : public shape { … };
class triangle : public shape { … };
class rectangle : public shape { … }; shape* create_shape(shape_type type)
{

switch (type) {
case shape_type::circle:
return new circle(…);
case shape_type::triangle:
return new triangle(…);
case shape_type::rectangle:
return new rectangle(…);

}
}

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

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

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

class shape_wrapper {
public:
explicit shape_wrapper(
shape* ptr = nullptr)
: ptr_(ptr) {}
~shape_wrapper()
{
delete ptr_;
}
shape* get() const { return ptr_; }
private:
shape* ptr_;
}; void foo()
{

shape_wrapper ptr_wrapper(
create_shape(…));

}

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

最新文章

  1. java多线程 生产者消费者模式
  2. docker --命令
  3. Nvelocity用法
  4. Docker中自动化搭建Hadoop2.6完全分布式集群
  5. 解决maven项目update project会更改jdk版本问题
  6. linux初学 :简易的shell脚本
  7. ZeroBrane Lua脚本编辑器代码自动补全
  8. 抓取锁的sql语句-第二次修改
  9. wdcp/wdlinux一键包的php5.3版本添加Zend.so 和Soap.so
  10. Servlet编码和解码
  11. SVG的基础使用
  12. python进阶------进程线程(四)
  13. Server 2008 R2远程桌面授权,解决120天过期问题
  14. VisualStudio2017集成GitHub
  15. 【转】通过 INotifyPropertyChanged 实现观察者模式
  16. Windows 10忘记登录密码不用怕,系统U盘/光盘轻松重置
  17. python之zip函数和sorted函数
  18. Chap3:文件系统中跳转[The Linux Command Line]
  19. win7 php nginx 启动命令
  20. 把图片上的文字转换成word文字?

热门文章

  1. 03 Java的数据类型分为两大类 类型转换 八大基本类型
  2. LGP5142题解
  3. git使用命令行保留原分支迁移代码仓库
  4. Centos7 环境下设置固定IP
  5. Prism 框架解读之一系列
  6. java web中统一结果返回封装类JsonResult
  7. 5月28日 python学习总结 CSS学习(二)
  8. FrameScan CMS漏洞扫描
  9. Nebula Graph 在网易游戏业务中的实践
  10. Linux系统常用的命令