微信小程序教程 web开发 bam windows powershell concurrency datepicker sms pmp视频 jquery通过class获取元素 android富文本框架 matlab定义空矩阵 一兆等于多少字节 android调试工具 python练习 mysql函数 python编程语言 java重写和重载的区别 javaif语句 javarandom javac java集合框架图 变量的类型 linux命令行 sql语句大全实例教程 js数组移除指定元素 彻底卸载mysql html5制作 疯狂的站长 android下载文件 mmap文件怎么打开 贪吃蛇c语言代码 苹果内存怎么看 伤害显示宏 ps二寸照片制作教程 cdr调和工具怎么用 生成海报 php队列 优酷网打不开 暴风城声望
当前位置: 首页 > 学习教程  > 编程语言

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

2020/11/24 11:09:37 文章标签: 测试文章如有侵权请发送至邮箱809451989@qq.com投诉后文章立即删除

目录 1、std::unique_ptr 2、 std::shared_ptr 3、std::weak_ptr 头文件<memory> 1、std::unique_ptr 声明&#xff1a; template<class T,class Deleter std::default_delete<T>> class unique_ptr;template <class T,class Deleter> class un…

目录

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 次
}

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;
}

运行结果:

再看一个关于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';

}

 

最后来看一个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";
}

 

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();
}

运行结果:

 


本文链接: http://www.dtmao.cc/news_show_400426.shtml

附件下载

相关教程

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?