天天看点

c++11:智能指针之std::unique_ptr、std::shared_ptr、std::weak_ptr1、std::unique_ptr2、std::shared_ptr3、std::weak_ptr

目录

1、std::unique_ptr

2、std::shared_ptr

3、std::weak_ptr

头文件<memory>

1、std::unique_ptr

声明:

template<class T,class Deleter = std::default_delete<T>> class unique_ptr;
template <class T,class Deleter> class unique_ptr<T[], Deleter>;

std::unique_ptr是通过指针占有并管理另一对象,并在unique_ptr离开作用域时释放该对象的智能指针。

在下列两者之一发生时用关联的删除器释放对象:

  • 销毁了管理的 unique_ptr 对象
  • 通过 operator= 或 reset() 赋值另一指针给管理的 unique_ptr 对象。

通过调用 get_deleter()(ptr) ,用潜在为用户提供的删除器释放对象。默认删除器用 delete 运算符,它销毁对象并解分配内存。

unique_ptr 亦可以不占有对象,该情况下称它为空 (empty)。

std::unique_ptr 有两个版本:

1) 管理单个对象(例如以 new 分配)

2) 管理动态分配的对象数组(例如以 new[] 分配)

类满足可移动构造 (MoveConstructible) 和可移动赋值 (MoveAssignable) 的要求,但不满足可复制构造 (CopyConstructible) 或可复制赋值 (CopyAssignable) 的要求。

注意点:

只有非 const 的 

unique_ptr

 能转移被管理对象的所有权给另一 

unique_ptr

 。若对象的生存期为 const std::unique_ptr 所管理,则它被限定在创建指针的作用域中。

std::unique_ptr

 常用于管理对象的生存期,包含:

  • 通过正常退出和经由异常退出两者上的受保证删除,提供异常安全,给处理拥有动态生存期的对象的类和函数
  • 传递独占的拥有动态生存期的对象的所有权到函数
  • 从函数获得独占的拥有动态生存期对象的所有权
  • 移动容器的元素类型,例如保有指向动态分配对象的指针的 std::vector (例如,若想要多态行为)
#include <iostream>
#include <vector>
#include <memory>
#include <cstdio>
#include <fstream>
#include <cassert>
#include <functional>

struct B {
  virtual void bar() { std::cout << "B::bar\n"; }
  virtual ~B() = default;
};
struct D : B
{
    D() { std::cout << "D::D\n";  }
    ~D() { std::cout << "D::~D\n";  }
    void bar() override { std::cout << "D::bar\n";  }
};

// 消费 unique_ptr 的函数能以值或以右值引用接收它
std::unique_ptr<D> pass_through(std::unique_ptr<D> p)
{
    p->bar();
    return p;
}

void close_file(std::FILE* fp) { std::fclose(fp); }

int main()
{
  std::cout << "unique ownership semantics demo\n";
  {
      auto p = std::make_unique<D>(); // p 是占有 D 的 unique_ptr
      auto q = pass_through(std::move(p));
      assert(!p); // 现在 p 不占有任何内容并保有空指针
      q->bar();   // 而 q 占有 D 对象
  } // ~D 调用于此

  std::cout << "Runtime polymorphism demo\n";
  {
    std::unique_ptr<B> p = std::make_unique<D>(); // p 是占有 D 的 unique_ptr
                                                  // 作为指向基类的指针
    p->bar(); // 虚派发

    std::vector<std::unique_ptr<B>> v;  // unique_ptr 能存储于容器
    v.push_back(std::make_unique<D>());
    v.push_back(std::move(p));
    v.emplace_back(new D);
    for(auto& p: v) p->bar(); // 虚派发
  } // ~D called 3 times

  std::cout << "Custom deleter demo\n";
  std::ofstream("demo.txt") << 'x'; // 准备要读的文件
  {
      std::unique_ptr<std::FILE, void (*)(std::FILE*) > fp(std::fopen("demo.txt", "r"),
                                                           close_file);
      if(fp) // fopen 可以打开失败;该情况下 fp 保有空指针
        std::cout << (char)std::fgetc(fp.get()) << '\n';
  } // fclose() 调用于此,但仅若 FILE* 不是空指针
    // (即 fopen 成功)

  std::cout << "Custom lambda-expression deleter demo\n";
  {
    std::unique_ptr<D, std::function<void(D*)>> p(new D, [](D* ptr)
        {
            std::cout << "destroying from a custom deleter...\n";
            delete ptr;
        });  // p 占有 D
    p->bar();
  } // 调用上述 lambda 并销毁 D

  std::cout << "Array form of unique_ptr demo\n";
  {
      std::unique_ptr<D[]> p{new D[3]};
  } // 调用 ~D 3 次
}
           
c++11:智能指针之std::unique_ptr、std::shared_ptr、std::weak_ptr1、std::unique_ptr2、std::shared_ptr3、std::weak_ptr

2、std::shared_ptr

std::shared_ptr 是通过指针保持对象共享所有权的智能指针。多个 shared_ptr 对象可占有同一对象。下列情况之一出现时销毁对象并释放其内存:

  • 最后剩下的占有对象的 shared_ptr 被销毁;
  • 最后剩下的占有对象的 shared_ptr 被通过 operator= 或 reset() 赋值为另一指针。

修改器:

reset 替换所管理的对象   // sptr.reset(new Foo); 指针sptr指向新的对象(new Foo),原对象解引用
swap 交换所管理的对象  //  sptr.reset(r) ;  交换 sptr 与 

r

 的内容

 观察器:

get 返回存储的指针
use_count 返回 

shared_ptr

 所指对象的引用计数
unique 检查所管理对象是否仅由当前 

shared_ptr

 的实例管理
owner_before //提供基于拥有者的共享指针排序,x.owner_before(q)  如果q被认为与x不同,并根据所有权以严格的弱顺序排在它之前,则返回true。

先来看一个owner_before的例子

// shared_ptr::owner_before
#include <iostream>
#include <memory>

int main () {
  int * p = new int (10);

  std::shared_ptr<int> a (new int (20));
  std::shared_ptr<int> aa (a);
  std::shared_ptr<int> b (a,p);  //a与b指向同样的内容,但是b不包含p
  std::shared_ptr<int> c (new int (20));

  std::cout << "comparing a and b...\n" << std::boolalpha;
  std::cout << "a=aa: " <<  (a==aa) << std::endl;  //a与aa指向同样的内容,相等
  std::cout << "a<b: " <<  (a<b) << std::endl;
  std::cout << "a=b: " <<  (a==b) << std::endl;
  std::cout << "a>b: " <<  (a>b) << std::endl;
  std::cout << "a>c: " <<  (a>c) << std::endl;
  std::cout << "a.owner_before(aa): " << a.owner_before(aa) << std::endl;
  std::cout << "aa.owner_before(a): " << aa.owner_before(a) << std::endl;
  std::cout << "a.owner_before(b): " << a.owner_before(b) << std::endl;
  std::cout << "b.owner_before(a): " << b.owner_before(a) << std::endl;
  std::cout << "a.owner_before(c): " << a.owner_before(c) << std::endl;  //a和c完全不同,且a在c之前
  std::cout << "c.owner_before(a): " << c.owner_before(a) << std::endl;

  delete p;
  return 0;
}
           

运行结果:

c++11:智能指针之std::unique_ptr、std::shared_ptr、std::weak_ptr1、std::unique_ptr2、std::shared_ptr3、std::weak_ptr

再看一个关于owner_before的例子 

#include <iostream>
#include <memory>

struct Foo {
    int n1;
    int n2;
    Foo(int a, int b) : n1(a), n2(b) {}
};
int main()
{
    auto p1 = std::make_shared<Foo>(1, 2);
    std::shared_ptr<int> p2(p1, &p1->n1);
    std::shared_ptr<int> p3(p1, &p1->n2);

    std::cout << std::boolalpha
              << "p2 < p3 " << (p2 < p3) << '\n'
              << "p3 < p2 " << (p3 < p2) << '\n'
              << "p2.owner_before(p3) " << p2.owner_before(p3) << '\n'
              << "p3.owner_before(p2) " << p3.owner_before(p2) << '\n';

    std::weak_ptr<int> w2(p2);
    std::weak_ptr<int> w3(p3);
    std::cout
//              << "w2 < w3 " << (w2 < w3) << '\n'  // won't compile
//              << "w3 < w2 " << (w3 < w2) << '\n'  // won't compile
              << "w2.owner_before(w3) " << w2.owner_before(w3) << '\n'
              << "w3.owner_before(w2) " << w3.owner_before(w2) << '\n';

}
           
c++11:智能指针之std::unique_ptr、std::shared_ptr、std::weak_ptr1、std::unique_ptr2、std::shared_ptr3、std::weak_ptr

最后来看一个shared_ptr的综合应用的实例 

#include <iostream>
#include <memory>
#include <thread>
#include <chrono>
#include <mutex>

struct Base
{
    Base() { std::cout << "  Base::Base()\n"; }
    // 注意:此处非虚析构函数 OK
    ~Base() { std::cout << "  Base::~Base()\n"; }
};

struct Derived: public Base
{
    Derived() { std::cout << "  Derived::Derived()\n"; }
    ~Derived() { std::cout << "  Derived::~Derived()\n"; }
};

void thr(std::shared_ptr<Base> p)
{
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::shared_ptr<Base> lp = p; // 线程安全,虽然自增共享的 use_count
    {
        static std::mutex io_mutex;
        std::lock_guard<std::mutex> lk(io_mutex);
        std::cout << "local pointer in a thread:\n"
                  << "  lp.get() = " << lp.get()
                  << ", lp.use_count() = " << lp.use_count() << '\n';
    }
}

int main()
{
    std::shared_ptr<Base> p = std::make_shared<Derived>();

    std::cout << "Created a shared Derived (as a pointer to Base)\n"
              << "  p.get() = " << p.get()
              << ", p.use_count() = " << p.use_count() << '\n';
    std::thread t1(thr, p), t2(thr, p), t3(thr, p);
    p.reset(); // 从 main 释放所有权
    std::cout << "Shared ownership between 3 threads and released\n"
              << "ownership from main:\n"
              << "  p.get() = " << p.get()
              << ", p.use_count() = " << p.use_count() << '\n';
    t1.join(); t2.join(); t3.join();
    std::cout << "All threads completed, the last one deleted Derived\n";
}
           
c++11:智能指针之std::unique_ptr、std::shared_ptr、std::weak_ptr1、std::unique_ptr2、std::shared_ptr3、std::weak_ptr

3、std::weak_ptr

std::weak_ptr 是一种智能指针,它对被 std::shared_ptr 管理的对象存在非拥有性(“弱”)引用。在访问所引用的对象前必须先转换为 std::shared_ptr。

std::weak_ptr 用来表达临时所有权的概念:当某个对象只有存在时才需要被访问,而且随时可能被他人删除时,可以使用 std::weak_ptr 来跟踪该对象。需要获得临时所有权时,则将其转换为 std::shared_ptr,此时如果原来的 std::shared_ptr 被销毁,则该对象的生命期将被延长至这个临时的 std::shared_ptr 同样被销毁为止。

std::weak_ptr 的另一用法是打断 std::shared_ptr 所管理的对象组成的环状引用。若这种环被孤立(例如无指向环中的外部共享指针),则 shared_ptr 引用计数无法抵达零,而内存被泄露。能令环中的指针之一为弱指针以避免此情况。

修改器:

reset 释放被管理对象的所有权
swap 交换被管理对象

观察器:

use_count 返回管理该对象的shared_ptr对象数量
expired 检查被引用的对象是否已删除
lock 创建被引用的对象的shared_ptr
owner_before 提供弱智真的基于拥有者顺序,参考shared_ptr
#include <iostream>
#include <memory>

std::weak_ptr<int> gw;

void f()
{
    if (auto spt = gw.lock()) { // 使用之前必须复制到 shared_ptr
        std::cout << *spt << "\n";
    }
    else {
        std::cout << "gw is expired\n";
    }
}

int main()
{
    {
        auto sp = std::make_shared<int>(42);
        gw = sp;

        f();
    }

    f();
}
           

运行结果:

c++11:智能指针之std::unique_ptr、std::shared_ptr、std::weak_ptr1、std::unique_ptr2、std::shared_ptr3、std::weak_ptr

继续阅读