智能指针

智能指针

C++ 智能指针是对普通指针的一种封装,具体的有四类指针, 而不是一种。按照需求使用指针有助于我们提高代码质量。

所有的智能指针都在追求一个东西:RAII(Resource acquisition is initialization), 资源获取即初始化。

scoped_ptr 作用域指针(boost 库) #

正如其名字指出的那样,在作用域结束的时候即自动释放的指针。

scoped_ptr 指针具有一些特殊的要求。

  1. scoped_ptr是noncopyable的,也就是无法复制。
  2. 不能作为容器中的元素,因为无法复制。

example #

#include <boost/scoped_ptr.hpp>
#include <iostream>

struct Shoe { ~Shoe() { std::cout << "Buckle my shoe\n"; } };

class MyClass {
    boost::scoped_ptr<int> ptr;
  public:
    MyClass() : ptr(new int) { *ptr = 0; }
    int add_one() { return ++*ptr; }
};

int main()
{
    boost::scoped_ptr<Shoe> x(new Shoe);
    MyClass my_instance;
    std::cout << my_instance.add_one() << '\n';
    std::cout << my_instance.add_one() << '\n';
}

// output
// 1
// 2
// Buckle my shoe 

比较特殊的地方是 1. x没有手动调用delete;2. MyClass的析构函数不需要释放ptr;

【参考文献】 #

  1. boost 库实现scoped_ptr: https://www.boost.org/doc/libs/1_50_0/libs/smart_ptr/scoped_ptr.htm

shared_ptr #

shared_ptr中所实现的本质是引用计数(reference counting),也就是说shared_ptr是支持复制的,复制一个shared_ptr的本质是对这个智能指针的引用次数加1,而当这个智能指针的引用次数降低到0的时候,该对象自动被析构。

需要特别指出的是,如果shared_ptr所表征的引用关系中出现一个环,那么环上所述对象的引用次数都肯定不可能减为0那么也就不会被删除,为了解决这个问题引入了weak_ptr。

example #

#include <memory>
using namespace std;

class F {};
class G : public F {};

shared_ptr<G> sp0(new G);   // okay, template parameter G and argument G*
shared_ptr<G> sp1(sp0);     // okay, template parameter G and argument shared_ptr<G>
shared_ptr<F> sp2(new G);   // okay, G* convertible to F*
shared_ptr<F> sp3(sp0);     // okay, template parameter F and argument shared_ptr<G>
shared_ptr<F> sp4(sp2);     // okay, template parameter F and argument shared_ptr<F>
shared_ptr<int> sp5(new G); // error, G* not convertible to int*
shared_ptr<int> sp6(sp2);   // error, template parameter int and argument shared_ptr<F>

【参考文献】 #

  1. shared_ptr 微软文档:https://docs.microsoft.com/zh-cn/cpp/standard-library/shared-ptr-class?view=msvc-170

weaked_ptr #

对weak_ptr起的作用,很多人有自己不同的理解,我理解的weak_ptr和shared_ptr的最大区别在于weak_ptr在指向一个对象的时候不会增加其引用计数,因此你可以用weak_ptr去指向一个对象并且在weak_ptr仍然指向这个对象的时候析构它,此时你再访问weak_ptr的时候,weak_ptr其实返回的会是一个空的shared_ptr。实际上,通常shared_ptr内部实现的时候维护的就不是一个引用计数,而是两个引用计数,一个表示strong reference,也就是用shared_ptr进行复制的时候进行的计数,一个是weak reference,也就是用weak_ptr进行复制的时候的计数。weak_ptr本身并不会增加strong reference的值,而strong reference降低到0,对象被自动析构。为什么要采取weak_ptr来解决刚才所述的环状引用的问题呢?需要注意的是环状引用的本质矛盾是不能通过任何程序设计语言的方式来打破的,为了解决环状引用,第一步首先得打破环,也就是得告诉C++,这个环上哪一个引用是最弱的,是可以被打破的,因此在一个环上只要把原来的某一个shared_ptr改成weak_ptr,实质上这个环就可以被打破了,原有的环状引用带来的无法析构的问题也就随之得到了解决。

【参考文献】 #

  1. https://www.zhihu.com/question/20368881/answer/14918675

auto_ptr #

这是标准库中的一种简单的智能指针,与shared_ptr 不同在于,指针在复制的时候会转移所有权,也就是一个对象只会由一个智能指针所拥有,在复制和传递参数的时候都会出现所有权转移的问题。

使用auto_ptr 作为成员函数可以有效的避免内存泄漏,当两个成员A和B在构造函数中申请资源,如果A成功而B失败了,则整个对象的构造函数是失败的,因此不会调用对象的析构函数,而A申请的资源无法得到释放。 如果使用auto_ptr 来封装A和B,则可以保证当auto_ptr被销毁的时候一定连带销毁自己持有的指针。

unique_ptr #

同一时刻只能又一个unique_ptr 指向同一个对象,不支持拷贝转移。指针析构的时候会一起析构所指向的对象。