智能指针

1. shared_ptr的基本用法

  1. 初始化

    #include <memory>
    #include <iostream>
    
    int main()
    {
        std::shared_ptr<int> p(new int(1));
        std::shared_ptr<int> p2 = p;
        std::shared_ptr<int> ptr;
        ptr.reset(new int(1));
    
        if (ptr)
        {
            std::cout << "ptr is not null";
        }
    
        return 0;
    }
    
  2. 获取原始指针

    std::shared_ptr<int> ptr(new int(1));
    int* p = ptr.get();
    
  3. 指定删除器

    #include <memory>
    #include <iostream>
    
    void DeleteIntPtr(int* p) {
        delete p;
    }
    
    template <typename T>
    std::shared_ptr<T> make_shared_array(size_t size) {
        return std::shared_ptr<T>(new T[size], std::default_delete<T[]>());
    }
    
    int main()
    {
        std::shared_ptr<int> p1(new int(1), DeleteIntPtr);
    
        std::shared_ptr<int> p2(new int, [](int* p) {delete p; });
    
        // 当shared_ptr管理动态数组时,需要指定删除器,因为shared_ptr的默认删除器不支持数组对象
        std::shared_ptr<int> p3(new int[10], [](int* p) {delete[] p; });
        // 可以将default_delete 作为删除器。default_delete的内部是通过调用delete来实现功能的
        std::shared_ptr<int> p4(new int[10], std::default_delete<int[]>());
    
        std::shared_ptr<int> p5 = make_shared_array<int>(10);
        std::shared_ptr<char> p6 = make_shared_array<char>(10);
    
        return 0;
    }
    
  4. 使用shared_ptr需要注意的问题

    • 不要用一个原始指针初始化多个shared_ptr,会引发多次释放指针

       int* ptr = new int;
       std::shared_ptr<int> p1(ptr);
       std::shared_ptr<int> p2(ptr);
      
    • 不要在函数实参中创建shared_ptr

      function(std::shared_ptr<int>(new int), g());
      

      因为 C++ 的函数参数的计算顺序在不同的编译器不同的调用约定下可能是不一样的,一般是从右到左,但也有可能是从左到右,所以,可能的过程是先 new int,然后调g( ),如果恰好 g( )发生异常,而 shared_ptr 还没有创建,则 int 内存泄露了,正确的写法应该是先创建智能指针。

    • 通过shared_from_this()返回this指针。不要将this指针作为shared_ptr返回出来,因为this指针本质上是一个裸指针,因此可能会导致重复析构。

      #include <memory>
      #include <iostream>
      
      struct A {
          std::shared_ptr<A> GetSelf()
          {
              return std::shared_ptr<A>(this); // don't do this
          }
      
      };
      int main()
      {
          std::shared_ptr<A> sp1(new A);
          std::shared_ptr<A> sp2 = sp1->GetSelf();
      
          return 0;
      }
      

      由于用同一个指针(this)构造了两个智能指针sp1 和sp2,而它们之间是没有任何关系的,在离开作用域之后this将会被构造的两个智能指针各自析构,导致重复析构的错误。正确返回this的shared_ptr的做法是:让目标类通过派生std::enable_shared_from_this<T>类,然后使用基类的成员函数shared_from_this来返回this的shared_ptr,看下面的示例。

      #include <memory>
      #include <iostream>
      
      struct A : public std::enable_shared_from_this<A> {
          std::shared_ptr<A> GetSelf()
          {
              return shared_from_this();
          }
      
      };
      int main()
      {
          std::shared_ptr<A> sp1(new A);
          std::shared_ptr<A> sp2 = sp1->GetSelf();
      
          return 0;
      }
      
    • 要避免循环引用。智能指针最大的一个陷阱就是循环引用,循环引用会导致内存泄漏。

      #include <memory>
      #include <iostream>
      
      struct A {
          std::shared_ptr<B> bptr;
          ~A() { std::cout << "A is deleted!" << std::endl; }
      };
      
      struct B {
          std::shared_ptr<A> aptr;
          ~B() { std::cout << "B is deleted!" << std::endl; }
      };
      int main()
      {
          std::shared_ptr<A> ap(new A);
          std::shared_ptr<B> bp(new B);
          ap->bptr = bp;
          bp->aptr = ap;
          return 0;
      }
      

2. unique_ptr独占的智能指针

unique_ptr是一个独占型的智能指针,它不允许其他的智能指针共享其内部的指针,不允许通过赋值将一个unique_ptr赋值给另外一个unique_ptr。下面这样是错误的:

std::unique_ptr<int> myPtr(new int(5));
std::unique_ptr<int> myOtherPtr = myPtr;

unique_ptr不允许复制,但可以通过函数返回给其他的unique_ptr,还可以通过std::move 来转移到其他的 unique_ptr,这样它本身就不再拥有原来指针的所有权了。

std::unique_ptr<int> myPtr(new int(5));
std::unique_ptr<int> myOtherPtr = std::move(myPtr);

unique_ptr 和 shared_ptr 相比,unique_ptr 除了独占性这个特点之外,还可以指向一个数组,代码如下:

std::unique_ptr<int []> ptr(new int[10]);
ptr[9] = 9;

std::shared_ptr<int []>ptr(new int[10]);是不合法的。

unique_ptr指定删除器和std::shared_ptr是有差别的。

std::shared_ptr<int> ptr(new int(1), [](int* p) {delete p; });  // 正确
std::unique_ptr<int> ptr(new int(1), [](int* p) {delete p; });  // 错误

std::unique_ptr<int, void(*)(int*)> ptr(new int(1), [](int* p) {delete p; });

std::unique_ptr<int, void(*)(int*)> ptr(new int(1), [&](int* p) {delete p; }); // 错误,捕获了变量

std::unique_ptr<int, std::function<void(int*)>> ptr(new int(1), [&](int* p) {delete p; });

自定义unique_ptr的删除器

#include <memory>
#include <functional>
#include <iostream>

struct MyDeleter
{
    void operator()(int* p)
    {
        std::cout << "delete" << std::endl;
        delete p;
    }
};

int main()
{
    std::unique_ptr<int, MyDeleter> p(new int(1));

    return 0;
}

3. weak_ptr弱引用的智能指针

弱引用指针weak_ptr是用来监视shared_ptr的,不会使引用计数加1,它不管理shared_ptr内部的指针,主要是为了监视shared_ptr 的生命周期,更像是shared_ptr的一个助手。weak_ptr没有重载操作符*和->,因为它不共享指针,不能操作资源,主要是为了通过shared_ptr 获得资源的监测权,它的构造不会增加引用计数,它的析构也不会减少引用计数,纯粹只是作为一个旁观者来监视shared_ptr中管理的资源是否存在。weak_ptr还可以用来返回 this 指针和解决循环引用的问题。

3.1. weak_ptr基本用法

  1. 通过use_count()方法来获取当前观测资源的引用计数。

    #include <memory>
    #include <iostream>
    
    int main()
    {
        std::shared_ptr<int> sp(new int(10));
        std::weak_ptr<int> wp(sp);
        std::cout << wp.use_count() << std::endl;
        return 0;
    }
    
  2. 通过expired()方法来判断所观测的资源是否已经被释放。

    #include <memory>
    #include <iostream>
    
    int main()
    {
        std::shared_ptr<int> sp(new int(10));
        std::weak_ptr<int> wp(sp);
        if (wp.expired()) {
            std::cout << "weak_ptr无效,所监视的智能指针已被释放" << std::endl;
        }
        else {
            std::cout << "weak_ptr有效" << std::endl;
        }
        return 0;
    }
    
  3. 通过lock方法来获取所监视的shared_ptr。

    #include <memory>
    #include <iostream>
    
    std::weak_ptr<int> gw;
    
    void f()
    {
        if (gw.expired())
        {
            std::cout << "gw is expired" << std::endl;
        }
        else
        {
            auto spt = gw.lock();
            std::cout << *spt << std::endl;
        }
    }
    
    int main()
    {
        {
            auto sp = std::make_shared<int>(42);
            gw = sp;
            f();
        }
        f();
        return 0;
    }
    

3.2. weak_ptr返回this指针

不能直接将this指针返回为shared_ptr,需要通过派生std::enableshared_from_this 类,并通过其方法 shared_from_this 来返回智能指针,原因是 std::enableshared_from_this 类中有一个 weak_ptr,这个 weak_ptr 用来观测 this 智能指针,调用 shared_from_this()方法时,会调用内部这个 weak_ptr 的 lock()方法,将所观测的 shared_ptr 返回。

需要注意的是,获取自身智能指针的函数仅在shared_ptr<T>的构造函数被调用之后才能使用,因为enable_shared_from_this内部的weak_ptr只有通过shared_ptr才能构造。

3.3. weak_ptr解决循环引用问题

#include <memory>
#include <iostream>

struct A;
struct B;

struct A {
    std::shared_ptr<B> bptr;
    ~A() { std::cout << "A is deleted!" << std::endl; }
};

struct B {
    std::weak_ptr<A> aptr;    // 改为weak_ptr
    ~B() { std::cout << "B is deleted!" << std::endl; }
};
int main()
{
    std::shared_ptr<A> ap(new A);
    std::shared_ptr<B> bp(new B);


    ap->bptr = bp;
    bp->aptr = ap;

    return 0;
}

4. 探索练习

#include <iostream>
#include <memory>


class TClass{
public:
    TClass() = default;
    TClass(const TClass& other) = default;
    TClass& operator=(const TClass& other) = default;
    TClass(TClass&& other) noexcept = default;
    TClass& operator=(TClass&& other) noexcept = default;
    ~TClass() = default;
};

void test1(std::shared_ptr<TClass>& p){
    p.reset();
}

void test2(std::shared_ptr<TClass> p){
    p.reset();
}

void test3(std::unique_ptr<TClass>& p){
    p.reset();
}

void test4(std::unique_ptr<TClass> p){
    p.reset();
}


int main()
{
    std::shared_ptr<TClass> m1 = std::make_unique<TClass>();
    std::shared_ptr<TClass> m2 = std::make_unique<TClass>();
    std::unique_ptr<TClass> m3 = std::make_unique<TClass>();
    std::unique_ptr<TClass> m4 = std::make_unique<TClass>();

    test1(m1);
    test5(m2);
    test3(m3);
    // test4(m4); // unique_ptr的拷贝构造函数标记为delete

    std::cout << "m1 is nullptr? " << (m1 == nullptr ? "true" : "false") << std::endl;    
    std::cout << "m2 is nullptr? " << (m2 == nullptr ? "true" : "false") << std::endl;    
    std::cout << "m3 is nullptr? " << (m3 == nullptr ? "true" : "false") << std::endl;    
    std::cout << "m4 is nullptr? " << (m4 == nullptr ? "true" : "false") << std::endl;    

    return 0;
}

results matching ""

    No results matching ""