C++ 使用智能指针的必要性
评判智能指针的标准
在C/C++中,指针很重要,却是麻烦的来源。如果一个指针建立是没有初始化,那就是野指针(Vaild Pointer);如果一个指针指向已经释放的内存,那就是空悬指针(Dangling Pointer)。
一些比 C/C++ 更高级的语言都要极力避开指针,比如 Java 使用了垃圾回收,让系统自动回收用户申请的资源。
C++ 的智能指针,始于 Boost 准标准库,在 C++11 正式加入到C++标准模板库(STL)中。
智能指针的核心思想是:“ 程序员负责申请资源,释放留给智能指针 ”。所以我们可以最直观的感受到智能指针的好处是:“ 申请资源一劳永逸,老板再也不用担心内存泄露 ”。
但如果我们对智能指针的理解仅限于此,我们就会受到很多反对的声音的干扰。
- “ 当年的 auto_ptr 多么垃圾 ”
- 智能会带来许多额外的开销,C/C++的效率高的优势荡然无存
- 智能指针的风格和原来的 new 格格不入
- 遇到某些复杂的数据结构,或者所有权不明确的场景,还是得裸指针来
但是,如果仅仅将智能理解为防止内存泄露,是十分片面的。在考虑是否使用智能指针的时候,我们应该看到,该项技术到底是弊大于利还是利大于弊?
多线程的情况
如果我们考虑到多线程的情况,那么智能指针的优势几乎是压倒性的。
如今有一个多线程的程序,多个线程共同持有一个对象。那么:
(1) 如何确保在对象析构的时候,没有其他线程正在使用对象资源呢?
(2) 如果确保在对象析构后,没有其他线程会后续使用对象资源呢?
对于第一个问题,我们可以在对象外部建立一套锁机制,在对象析构和引用对象的时候通过锁来确保排他性访问。
对于第二个问题,我们也可以在对象外部奖励一套信号量机制,每当有线程应用对象,便对信号量进行P操作来计数,使用完毕时进行V操作。
但是,如果对于每个共享资源,我们新建一套保护机制,那么对于程序员来说是非常大的负担,同时也容易出错。
智能指针恰恰帮程序员解决了这一痛点,因为智能指针有本身就是通过一套原子性的计数机制实现的。
下面是一些 shared_ptr 的笔记。
shared_ptr 的申请对象
使用 shared_ptr 申请对象最好的方法是通过 make_shared<T>(args)
函数返回 shared_ptr。其中 args 是对象的构造函数的参数。
shared_ptr<T> sp = make_shared<T>( args );
// 相当于 T *p = new T( args );
shared_ptr 的拷贝构造
- 如果已经申请对象,可以用拷贝构造使多个 shared_ptr 指向同一个对象。
- 当进行拷贝或赋值操作时,每个shared_ptr都会记录有多少个其他shared_ptr指向相同的对象。
- 当我们给shared_ptr赋予一个新值或是shared_ptr被销毁时,计数器就会递减。
- 当最后一个对象的最后一个shared_p