文章目录
- 一、原始指针容易发生内存泄漏
- 二、使用构造函数和析构函数解决内存泄漏
- 三、shared_ptr共享的智能指针
- 四、weak_ptr弱引用的智能指针
- 五、unique_ptr独占的智能指针
- 六、性能与安全的权衡
- 七、智能指针的简单实现
- 八、附录:全部代码
一、原始指针容易发生内存泄漏
C++ 程序设计中使用堆内存是非常频繁的操作,堆内存的申请和释放都由程序员自己管理。但使用普通指针,容易造成内存泄露(忘记释放)、二次释放、程序发生异常时内存泄露等问题等。所以 C++11 就引入了智能指针。
C 语言中最常使用的是malloc()函数分配内存,free()函数释放内存,而 C++ 中对应的是new、delete关键字。malloc()只是分配了内存,而new则更进一步,不仅分配了内存,还调用了构造函数进行初始化。
二、使用构造函数和析构函数解决内存泄漏
C++ 中的构造函数和析构函数十分强大,可以使用构造和析构解决上面的内存泄漏问题。就算发生了异常,也能够保证析构函数成功执行。但是在多个引用和多线程的时候还是会出现问题。
C++11 中引入了智能指针(Smart Pointer),它利用了一种叫做 RAII(资源获取即初始化)的技术将普通的指针封装为一个栈对象。当栈对象的生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄漏。这使得智能指针实质是一个对象,行为表现的却像一个指针。
智能指针主要分为shared_ptr、unique_ptr和weak_ptr三种,使用时需要引用头文件。C++98 中还有auto_ptr,基本被淘汰了,不推荐使用。而 C++11 中shared_ptr和weak_ptr都是参考boost库实现的。
三、shared_ptr共享的智能指针
四、weak_ptr弱引用的智能指针
shared_ptr的一个最大的陷阱是循环引用,循环引用会导致堆内存无法正确释放,导致内存泄漏。
要想解决上面循环引用的问题,只能引入新的智能指针std::weak_ptr。std::weak_ptr有什么特点呢?与std::shared_ptr最大的差别是在赋值的时候,不会引起智能指针计数增加。
五、unique_ptr独占的智能指针
unique_ptr相对于其他两个智能指针更加简单,它和shared_ptr使用差不多,但是功能更为单一,它是一个独占型的智能指针,不允许其他的智能指针共享其内部的指针,更像原生的指针(但更为安全,能够自己释放内存)。不允许赋值和拷贝操作,只能够移动。
六、性能与安全的权衡
使用智能指针虽然能够解决内存泄漏问题,但是也付出了一定的代价。以shared_ptr举例:
- shared_ptr的大小是原始指针的两倍,因为它的内部有一个原始指针指向资源,同时有个指针指向引用计数。
- 引用计数的内存必须动态分配。虽然一点可以使用make_shared()来避免,但也存在一些情况下不能够使用make_shared()。
- 增加和减小引用计数必须是原子操作,因为可能会有读写操作在不同的线程中同时发生。