C/C++开发,无可避免的内存管理(篇四)-智能指针备选

news2024/9/28 23:38:13

一、智能指针

        采用C/C++开发堆内存管理无论是底层开发还是上层应用,无论是开发新手,还是多年的老手,都会不自觉中招,尤其是那些不是自己一手经历的代码,要追溯问题出在哪里更是个麻烦事。C/C++程序常常会遇到程序突然退出,占用内存越来越多、定期重启等症状,可能都是堆内存管理没有正确处理好内存分配和释放,出现野指针、重复释放、内存泄漏等语法错误:

  • 野指针,一些内存单元已经被释放,之前指向它的指针确还在被使用,导致无法预测的错误。
  • 重复释放,程序试图去释放已经被释放过的内存单元,或者释放已经被重新分配过的内存单元,导致重复释放错误,系统会引出触发大量的错误及诊断信息输出。
  • 内存泄漏,不再需要使用的内存单元没有被释放,程序不断地重复运行这些指令,会导致内存没被回收而占用大量内存,造成程序内存使用不断在增加。

        而智能指针就是用来解决程序资源生存期管理的问题(尤其是动态分配的对象,堆内存管理)。 智能指针有各种不同的风格。多数都有一种共同的关键特性:自动资源管理。这种特性可能以不同的方式出现:如动态分配对象的生存期控制,和获取及释放资源 (文件,网络连接)。大多智能指针主要针对第一种情况,它们保存指向动态分配对象的指针,并在正确的时候删除这些对象。除了这些功能以外,智能指针要尽可能少做其他工作。虽然可以通过程序设计,使得它们增加功能,覆盖所有资源管理的不同情况,但是需要付出代价的,通常这样的解决方案意味着更高的复杂性。

        如果能显式管理内存,其实在性能上是有一定的优势的,但是也意味着内存异常风险增加,尤其在多线程编程领域,内存管理不佳的情况可能会更加严重。为了让开发人员专注于业务开发,摆脱内存管理的细节,c++标准库引入了智能指针。

二、auto_ptr

        在C++98,智能指针通过一个类模板“auto_ptr ” 来实现的,它以对象的方式管理堆分配的内存,并在适当的时机,释放所获得的堆内存。开发者使用auto_ptr模板时,将new操作返回的指针作为其初始值即可,在后面就不再需要开发者调用delete手动释放堆内存了。

#include <memory>

std::auto_ptr<int> pi(new int);
*pi = 100;

        这在一定程度上避免了堆内存忘记释放造成的问题。

        2.1 仿auto_ptr 的类模板

        下面来看看auto_ptr 模板的具体实现原理。创建一个类似auto_ptr 模板的AutoPtr类模板,提供显式声明,构造时需要传入类型指针,整个AutoPtr类模板的主要作用就是管理这个指针(指向模板参数类型的指针)的生存周期。

#ifndef _AUTO_PTR_H_
#define _AUTO_PTR_H_

#include <iostream>

template<typename T>
class AutoPtr
{
public:
    explicit AutoPtr(T* p=0) : m_ptr(p) {
        std::cout << "AutoPtr create!\n";
     };
    ~AutoPtr() { 
        std::cout << "AutoPtr delete!\n";
        delete m_ptr; 
    };
    T& operator*() const { 
        return *m_ptr; 
    };
    T* operator->() const { 
        return m_ptr;
    };
    
private:
    T* m_ptr; // dumb pointer
};

void func1(void)
{
    AutoPtr<int> a(new int(100));
    std::cout << "*a = " << *a << "\n";
}

#include "autoptr.cpp"
#endif // _AUTO_PTR_H_

        上述代码就将一个指针放置在一个对象中管理,而C/C++语言中,对于对象会对其自动释放内存操作。所以AutoPtr类模板的本质就是将指向T类型的指针伪装成一个对象,然后让C/C++语法管理对象一样间接管理了该指针。智能指针可以像普通指针那样使用,因为其提供了operator*和operator->操作符,满足向普通指针一样的"."和"->"功能,使得使用AutoPtr<T>对象和使用T*指针一样,没有改变原来使用T*指针的语法习惯和结构。

#include "autoptr.h"
/*
void func1(void)
{
    AutoPtr<int> a(new int(100));
    std::cout << "*a = " << *a << "\n";
}
*/
int main(int argc, char* argv[])
{
    func1();
    return 0;
}
//out log
AutoPtr create!
*a = 100
AutoPtr delete!

        上述代码中,在一个函数内创建一个AutoPtr<int>对象,因为是对象,函数结束后(离开作用域)会自动释放,因此调用了AutoPtr的析构函数,因而间接释放int*指针指向的内存。

        2.2 auto_ptr 模板应用缺陷

        当然标准库中的auto_ptr 模板具有更多的功能:

get() //获取智能指针托管的指针地址
release() //取消智能指针对动态内存的托管
reset(T* ptr_=nullptr) //重置智能指针托管的内存地址,默认为null

        不过auto_ptr 因为拷贝时返回一个左值,复制或者赋值都会改变资源的所有权,在STL容器中使用auto_ptr存在着重大风险,因为容器内的元素必须支持可复制和可赋值,不支持对象数组的内存管理和不能调用delete[]等缺陷问题,在C++11标准中,被废弃了。对于auto_ptr 开发者保持了解即可,这是为了阅读一些旧代码需要,但是自行编写代码时,还是不要再使用auto_ptr ,而采用c++11后的智能指针吧。

三、C++11 的智能指针

        3.1 C++11 智能指针应用

         C++11 增加unique_ptr、shared_ptr 和weak_ptr,定义于头文件 <memory>。

  • unique_ptr,C++11用更严谨的unique_ptr 取代了auto_pt,和 auto_ptr用法几乎一样,除了拥有独有对象所有权语义的智能指针和一些特殊用法。
  • shared_ptr,进行对象的生存期自动管理,拥有共享对象所有权语义的智能指针,使得分享资源所有权变得有效且安全。
  • weak_ptr,可以安全地观测共享资源,对被 std::shared_ptr 管理的对象存在非拥有性(弱)引用,避免了悬挂的指针。

        在探究这几个智能指针的原理前,先看看它们的用法和auto_ptr 又有那些区别:

//ptrc11_test.h
#include <memory>
//#include <vector>
using namespace std;

void out_shared(weak_ptr<int> &wp)
{
    shared_ptr<int> sp = wp.lock();
    if(nullptr!=sp)
    {
        cout << "*wp1 = " << *sp << "\n";//20
    }else{
        cout << "pointer is invalid.\n";//
    }
}

void func2(void)
{
    unique_ptr<int> up1(new int(10));//
    //unique_ptr<int> up2=up1;//error,不能通过编译,无法赋值
    //unique_ptr<int> up3(up1);//error,不能通过编译
    cout << "*up1 = " << *up1 << "\n";//10
    unique_ptr<int> up3 = move(up1);//up3获得数据10唯一的unique_ptr智能指针
    cout << "*up3 = " << *up3 << "\n";//OK,10
    //cout << "*up1 = " << *up1 << "\n";//error,能编译但运行时出错
    up3.reset();    //显式释放内存
    up1.reset();    //OK,可编译,不会导致运行时错误
    //cout << "*up3 = " << *up3 << "\n";//error,能编译但运行时出错

    shared_ptr<int> sp1(new int(20));
    shared_ptr<int> sp2=sp1;    //OK
    weak_ptr<int> wp1 = sp1;//指向shared_ptr<int>所指对象
    cout << "*sp1 = " << *sp1 << "\n";//20
    cout << "*sp2 = " << *sp2 << "\n";//20
    out_shared(wp1);
    //
    sp1.reset();
    //cout << "*sp1 = " << *sp1 << "\n";//error,能编译但运行时出错
    cout << "*sp2 = " << *sp2 << "\n";//OK,20
    out_shared(wp1);
    //
    sp2.reset();
    out_shared(wp1);
}

#include "ptrc11_test.h"
int main(int argc, char* argv[])
{
    func2();
    return 0;
}
//out log
*up1 = 10
*up3 = 10
*sp1 = 20
*sp2 = 20
*wp1 = 20
*sp2 = 20
*wp1 = 20
pointer is invalid.

       上述代码,让智能指针指向一个int*指针指向的对象,由于每个智能指针都重载了*运算符,因此可以使用个*智能指针的方式访问所分配的堆内存。同时智能指针除了退出作用域自动释放堆内存外,还提供了reset成员函数主动释放其拥有的堆内存。从堆内存管理功能上来说,unique_ptr和shared_ptr和原来的auto_ptr是保持一致的。

        所区别的是,unique_ptr对所指向独享的内存是独占模式,不能与其他unique_ptr类型指针共享所指向的对象的内存。每个unique_ptr都是唯一占有所指向的对象内存,因此不能直接赋值给其他unique_ptr。但是这种独占所有权可以通过std::move函数来转移,转移后,旧unique_ptr指针就失去该对象内存的所有权,不能再使用,否则会运行错误。

        如果我们了解过单体类(单体模式)就比较好理解unique_ptr的本质是什么。unique_ptr就是一个删除了拷贝构造函数、保留了移动构造函数的指针封装类型。开发者仅可以使用右值对unique_ptr对象进行构造,一旦构造成功,它就获得了右值对象的指针,而右值对象则失去了对指针的“所有权”。

        shared_ptr正如其名一样,是允许多个shared_ptr指针共享同一个堆分配对象的内存的。它实际上就我们设计类时引入了计数方式。即一个shared_ptr指向了该对象的内存,计数+1,相应地,如果一个shared_ptr放弃了指向该对象内存,则计数-1。所以shared_ptr对象的reset成员就是将计数-1而已,只有计数归零时,shared_ptr才会真正释放所占有的堆内存空间。

        智能指针weak_ptr会复杂一些,由于shared_ptr是shared_ptr的弱引用,因此一般是与shared_ptr配套起来使用。它可以指向shared_ptr指针指向的对象内存,但却不拥有该对象内存。在实际使用weak_ptr时,是通过函数成员lock返回一个weak_ptr对象(所指对象内存无效时,返回nullptr)。这可以用来辅助验证shared_ptr智能指针的有效性。上述代码中两个shared_ptr都主动释放对象内存的所有权后,weak_ptr的lock返回空指针。

        3.2 unique_ptr智能指针

        unique_ptr定义于头文件<memory>,std::unique_ptr 有两个版本,声明如下:

  1. 管理单个对象(例如以 new 分配)。
  2. 管理动态分配的对象数组(例如以 new[] 分配。
template<class T,class Deleter = std::default_delete<T> > 
class unique_ptr;

template <class T,class Deleter> 
class unique_ptr<T[], Deleter>;

//对象定义
unique_ptr<int> array(new int); 
unique_ptr<int[]> array(new int[5]);

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

  • 离开作用域,调用析构销毁了管理的 unique_ptr 对象,通过调用 get_deleter()(ptr) ,用潜在为用户提供的删除器释放对象。默认删除器用 delete 运算符,它销毁对象并解分配内存。
  • 通过 operator= 或 reset() 赋值另一指针给管理的 unique_ptr 对象。只有非 const 的 unique_ptr 能转移被管理对象的所有权给另一 unique_ptr 。若对象的生存期为 const std::unique_ptr 所管理,则它被限定在创建指针的作用域中。

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

unique_ptr<int> pu1;

         unique_ptr提供移动构造 (MoveConstructible) 和移动赋值 (MoveAssignable) 的操作,但不能复制构造 (CopyConstructible) 或复制赋值 (CopyAssignable) 的操作,std::unique_ptr 常用于管理对象的生存期,具有以下应用:

  • 通过正常退出和经由异常退出两者上的受保证删除,提供异常安全,给处理拥有动态生存期的对象的类和函数
  • 两个指针不能指向同一个资源无法进行左值unique_ptr复制构造,也无法进行左值复制赋值操作,允许临时右值赋值构造和赋值,通过move函数实现
  • 传递独占的拥有动态生存期的对象的所有权到函数
  • 从函数获得独占的拥有动态生存期对象的所有权
  • 在容器中保存指针是安全的,作为具移动容器的元素类型,例如保有指向动态分配对象的指针的 std::vector (例如,若想要多态行为)。

        unique_ptr作为容器元素是安全的,但不允许直接赋值。

//ptrc11_test.h
void func3(void)
{
    vector<unique_ptr<string>> u_vec;
    unique_ptr<string> pu1(new string("I'm Pu1"));
    unique_ptr<string> pu2(new string("I'm Pu2"));
    u_vec.push_back(std::move(pu1));
    u_vec.push_back(std::move(pu2));
    cout << "u_vec.at(0):" << *u_vec.at(0) << endl;
    cout << "u_vec[1]:" << *u_vec[1] << endl;
    //u_vec[0] = u_vec[1]; /* 不允许直接赋值 */
    u_vec[0] = std::move(u_vec[1]); // 需要使用move修饰,标记对象内存所有权已被转移
    cout << "u_vec.at(0):" << *u_vec.at(0) << endl;
    //cout << "u_vec[1]:" << *u_vec[1] << endl; //运行错误,对象内存所有权已被转移
    string *str_ = new string("I'm str");
    u_vec[1].reset(str_);   //
    cout << "u_vec[1]:" << *u_vec[1] << endl; //OK,获得新的对象内存所有权
    cout << "*str_:" << *str_ << endl;//Ok
    u_vec[0] = std::move(u_vec[1]); // 需要使用move修饰,标记对象内存所有权已被转移
    cout << "u_vec.at(0):" << *u_vec.at(0) << endl;
    //cout << "u_vec[1]:" << *u_vec[1] << endl; //运行错误,对象内存所有权已被转移
    cout << "*str_:" << *str_ << endl;//Ok
}

//out log
u_vec.at(0):I'm Pu1
u_vec[1]:I'm Pu2
u_vec.at(0):I'm Pu2
u_vec[1]:I'm str
*str_:I'm str
u_vec.at(0):I'm str
*str_:I'm str

        完整认识unique_ptr类模板:

成员类型         定义 
pointer         若该类型存在则为 std::remove_reference<Deleter>::type::pointer ,否则为 T* 。必须满足可空指针 (NullablePointer) 。 
element_type    T ,此 unique_ptr 所管理的对象类型 
deleter_type    Deleter ,函数对象或到函数或到函数对象的左值引用,会从析构函数调用 

成员函数
(构造函数)      构造新的unique_ptr,public
(析构函数)      析构所管理的对象,如果存在的话,public
operator=      为unique_ptr赋值,public

修改器
release        返回一个指向被管理对象的指针,并释放所有权,public
reset          替换被管理对象,public
swap           交换被管理对象,public
 
观察器
get            返回指向被管理对象的指针,public 
get_deleter    返回用于析构被管理对象的删除器,public

operator bool  检查是否有关联的被管理对象,public

单对象版本,unique_ptr<T>
operator*     解引用指向被管理对象的指针,public
operator->    解引用指向被管理对象的指针,public

数组版本, unique_ptr<T[]>
operator[]    提供到被管理数组的有索引访问,public

非成员函数
make_unique   创建管理一个新对象的独占指针(C++14)
make_unique_for_overwrite 创建管理一个新对象的独占指针(C++20,函数模板)

operator==
operator!=    (C++20 中移除)
operator<     与另一个 unique_ptr 或 nullptr 进行比较,(函数模板) 
operator<=
operator>
operator>=
operator<=>   (C++20)
operator<<    (C++20) 输出被管理指针的值到输出流,(函数模板) 

std::swap(std::unique_ptr) (C++11),特化std::swap算法(函数模板) 

辅助类
std::hash<std::unique_ptr> (C++11)std::unique_ptr 的散列支持(类模板特化) 

        unique_ptr智能指针应用:

#ifndef _UNIQUE_PTR_TEST_H_
#define _UNIQUE_PTR_TEST_H_

#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); }
 
void func4(void)
{
  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 次
}
#endif
//out log
unique ownership semantics demo
D::D
D::bar
D::bar
D::~D
Runtime polymorphism demo
D::D
D::bar
D::D
D::D
D::bar
D::bar
D::bar
D::~D
D::~D
D::~D
Custom deleter demo
x
Custom lambda-expression deleter demo
D::D
D::bar
destroying from a custom deleter...
D::~D
Array form of unique_ptr demo
D::D
D::D
D::D
D::~D
D::~D
D::~D

        3.3 shared_ptr智能指针

        std::shared_ptr智能指针同样定义于头文件<memory>,声明如下:

//  (C++11 起) 
template< class T > class shared_ptr;

        shared_ptr 能在存储指向一个对象的指针时共享另一对象的所有权。此特性能用于在占有其所属对象时,指向成员对象。存储的指针为 get() 、解引用及比较运算符所访问。

        std::shared_ptr智能指针采用了对象引用计数策略,可以记录引用特定内存对象的智能指针数量,当复制或拷贝时,引用计数加1,当智能指针析构时,引用计数减1,如果计数为零,代表已经没有指针指向这块内存,那么我们就释放对象内存。

        std::shared_ptr 是多个 shared_ptr 对象可占有同一对象。下列情况之一出现时销毁对象并解分配其内存,被管理指针是在 use_count 抵达零时传递给删除器者。:

  • 最后剩下的占有对象的 shared_ptr 被销毁,用 delete 表达式或在构造期间提供给 shared_ptr 的定制删除器销毁对象;
  • 最后剩下的占有对象的 shared_ptr 被通过 operator= 或 reset() 赋值为另一指针。

        shared_ptr 亦可不占有对象,该情况下称它为空 (empty) (空 shared_ptr 可拥有非空存储指针,若以别名使用构造函数创建它)。

shared_ptr<int> sp1;

        引用计数通过use_count成员函数获取:

void func5(void)
{
    int *pi = new int(100);
    shared_ptr<int> sp1;
    shared_ptr<int> sp2(pi);
    cout << "sp1.use_count() = " << sp1.use_count() << "\n";
    cout << "sp2.use_count() = " << sp2.use_count() << "\n";
    sp1 = sp2;
    cout << "sp1.use_count() = " << sp1.use_count() << "\n";
    cout << "sp2.use_count() = " << sp2.use_count() << "\n";
    shared_ptr<int> sp3(sp1);
    cout << "sp1.use_count() = " << sp1.use_count() << "\n";
    cout << "sp2.use_count() = " << sp2.use_count() << "\n";
    cout << "sp3.use_count() = " << sp3.use_count() << "\n";
}
//out log
sp1.use_count() = 0
sp2.use_count() = 1
sp1.use_count() = 2
sp2.use_count() = 2
sp1.use_count() = 3
sp2.use_count() = 3
sp3.use_count() = 3

        在典型的实现中, std::shared_ptr 只保有二个指针:

  • get() 所返回的指针
  • 指向控制块的指针

        控制块是一个动态分配的对象,其中包含:

  • 指向被管理对象的指针或被管理对象本身
  • 删除器(类型擦除)
  • 分配器(类型擦除)
  • 占有被管理对象的 shared_ptr 的数量
  • 涉及被管理对象的 weak_ptr 的数量

        shared_ptr 的所有特化满足可复制构造 (CopyConstructible) 、可复制赋值 (CopyAssignable) 和可小于比较 (LessThanComparable) 的要求并可按语境转换为 bool 。

        std::shared_ptr 可以用于不完整类型 T 。但参数为裸指针的构造函数( template<class Y> shared_ptr(Y*) )和 template<class Y> void reset(Y*) 成员函数只可以用指向完整类型的指针调用(注意 std::unique_ptr 可以从指向不完整类型的裸指针构造)。

        shared_ptr 不确保线程绝对安全,需要使用者自行负责多线程下其使用语境与安全设计。多个线程能在 shared_ptr 的不同实例上调用所有成员函数(包含复制构造函数与复制赋值)而不附加同步,即使这些实例是副本,且共享同一对象的所有权。若多个执行线程访问同一 shared_ptr 而不同步,且任一线程使用 shared_ptr 的非 const 成员函数,则将出现数据竞争;原子函数的 shared_ptr 特化能用于避免数据竞争。

        shared_ptr类模板包含以下功能支持:

成员类型          定义 
element_type     T (C++17 前) ,std::remove_extent_t<T> (C++17 起) 
  
weak_type        (C++17 起) std::weak_ptr<T> 

成员函数
(构造函数)       构造新的 shared_ptr,public
(析构函数)       如果没有更多 shared_ptr 指向持有的对象,则析构对象,public
operator=       对 shared_ptr 赋值,public

修改器
reset          替换所管理的对象,public
swap           交换所管理的对象,public

观察器
get            返回存储的指针,public

operator*      解引用存储的指针,public
operator->     解引用存储的指针,public
operator[]     (C++17),提供到被存储数组的带下标访问,public
use_count      返回 shared_ptr 所指对象的引用计数,public
unique         (C++20 前),检查所管理对象是否仅由当前 shared_ptr 的实例管理,public
operator bool  检查是否有关联的管理对象,public
owner_before   提供基于拥有者的共享指针排序,public

非成员函数
make_shared    创建管理一个新对象的共享指针(函数模板) 
make_shared_for_overwrite 创建管理一个新对象的共享指针(函数模板) , (C++20)

allocate_shared 创建管理一个用分配器分配的新对象的共享指针(函数模板) 
allocate_shared_for_overwrite 创建管理一个用分配器分配的新对象的共享指针(函数模板) ,(C++20)

static_pointer_cast    应用 static_cast到被存储指针(函数模板)
dynamic_pointer_cast   应用 dynamic_cast到被存储指针(函数模板)
const_pointer_cast     应用 const_cast到被存储指针(函数模板)
reinterpret_pointer_cast 应用 reinterpret_cast到被存储指针(函数模板),(C++17)

get_deleter    返回指定类型中的删除器,若其拥有(函数模板) 

operator==    (函数模板) 
operator!=    (C++20 中移除) 与另一个 shared_ptr 或 nullptr 进行比较
operator<     (C++20 中移除)
operator<=    (C++20 中移除)
operator>     (C++20 中移除)
operator>=    (C++20 中移除)
operator<=>   (C++20)(函数模板) 
operator<<    将存储的指针的值输出到输出流(函数模板) 

std::swap(std::shared_ptr) (C++11),特化 std::swap 算法(函数模板) 
/*下列,(函数模板) ,特化的原子操作,(C++20 中弃用)*/
std::atomic_is_lock_free(std::shared_ptr)
std::atomic_load(std::shared_ptr)
std::atomic_load_explicit(std::shared_ptr)
std::atomic_store(std::shared_ptr)
std::atomic_store_explicit(std::shared_ptr)
std::atomic_exchange(std::shared_ptr)
std::atomic_exchange_explicit(std::shared_ptr)
std::atomic_compare_exchange_weak(std::shared_ptr)
std::atomic_compare_exchange_strong(std::shared_ptr)
std::atomic_compare_exchange_weak_explicit(std::shared_ptr)
std::atomic_compare_exchange_strong_explicit(std::shared_ptr)

辅助类
std::hash<std::shared_ptr> (C++11),std::shared_ptr 的散列支持(类模板特化) 
std::atomic<std::shared_ptr> (C++20),原子共享指针(类模板特化) 

      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';
    }
}
 
void func6(void)
{
    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";
}
//g++ main.cpp -o test.exe -std=c++11 -pthread
//out log
  Base::Base()
  Derived::Derived()
Created a shared Derived (as a pointer to Base)
  p.get() = 0x1fd6068, p.use_count() = 1
Shared ownership between 3 threads and released
ownership from main:
  p.get() = 0, p.use_count() = 0
local pointer in a thread:
  lp.get() = 0x1fd6068, lp.use_count() = 4
local pointer in a thread:
  lp.get() = 0x1fd6068, lp.use_count() = 3
local pointer in a thread:
  lp.get() = 0x1fd6068, lp.use_count() = 2
  Derived::~Derived()
  Base::~Base()
All threads completed, the last one deleted Derived

        以调用 std::make_shared 或 std::allocate_shared 创建 shared_ptr 时,以单次分配创建控制块和被管理对象。被管理对象在控制块的数据成员中原位构造。通过 shared_ptr 构造函数之一创建 shared_ptr 时,被管理对象和控制块必须分离分配。此情况中,控制块存储指向被管理对象的指针。

        shared_ptr 持有的指针是通过 get() 返回的;而控制块所持有的指针/对象则是最终引用计数归零时会被删除的那个。两者并不一定相等。

        shared_ptr 的析构函数会将控制块中的 shared_ptr 计数器减一,如果减至零,控制块就会调用被管理对象的析构函数。但控制块本身直到 std::weak_ptr 计数器同样归零时才会释放。若有共享指针指向同一控制块,则自增弱指针计数。

        为满足线程安全要求,引用计数器典型地用等价于用 std::memory_order_relaxed 的 std::atomic::fetch_add 自增(自减要求更强的顺序,以安全销毁控制块)。

        3.4 weak_ptr智能指针

        std::weak_ptr智能指针,定义于头文件 <memory>,声明如下:

//c++11起
template< class T > class 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 引用计数无法抵达零,而内存被泄露。能令环中的指针之一为弱指针以避免此情况。

         std::weak_ptr 指针功能设计如下:

成员类型            定义 
element_type       T (C++17 前) ;std::remove_extent_t<T>, (C++17 起) 
  
成员函数
(构造函数)          构造新的weak_ptr,public
(析构函数)          销毁 weak_ptr,public
operator=          为weak_ptr赋值,public

修改器
reset             释放被管理对象的所有权,public
swap              交换被管理对象,public

观察器
use_count         返回管理该对象的 shared_ptr 对象数量,public
expired           检查被引用的对象是否已删除,public
lock              创建管理被引用的对象的shared_ptr,public
owner_before      提供弱指针的基于拥有者顺序,public

非成员函数
std::swap(std::weak_ptr) (C++11), 特化 std::swap 算法(函数模板) 

辅助类
std::atomic<std::weak_ptr> (C++20),原子弱指针(类模板特化) 

        weak_ptr一般和std::shared_ptr一同使用, weak_ptr被用于指向控制块的指针和作为构造来源的shared_ptr的存储指针。需要用分离的存储指针确保 shared_ptr 和 weak_ptr 间的来回转化正确进行,即使对于别名使用的 shared_ptr 。不可能不经将 weak_ptr 中的存储指针锁入 shared_ptr 就访问它。

#ifndef _WEAK_PTR_H_
#define _WEAK_PTR_H_
//g++ main.cpp -o test.exe -std=c++11
#include <iostream>
#include <memory>
 
using namespace std;

void out_shared(weak_ptr<int> &wp)
{
    shared_ptr<int> sp = wp.lock();
    if(nullptr!=sp)
    {
        cout << "*wp1 = " << *sp << "\n";//20
    }else{
        cout << "pointer is invalid.\n";//
    }
}

void func7(void)
{
    std::weak_ptr<int> gw;
    {
        auto sp = std::make_shared<int>(42);
        gw = sp;
 
        out_shared(gw);
    }
 
    out_shared(gw);
};

#endif //_WEAK_PTR_H_
//out log
*wp1 = 42
pointer is invalid.

四、 智能指针辅助类

        c++11的头文件 <memory>除了定义上述三种智能指针外,还为这些智能指针提供了辅助类型:

owner_less (C++11),提供基于所有者的,共享指针和弱指针的混合类型的排序(类模板) 

enable_shared_from_this (C++11),允许对象创建指代自身的 shared_ptr(类模板) 

bad_weak_ptr (C++11),访问指向已销毁对象的 weak_ptr 时抛出的异常(类) 

default_delete (C++11),unique_ptr 的默认删除器,(类模板) 

        4.1 owner_less类

        owner_less在memory文件内声明如下:

template< class T > struct owner_less<std::shared_ptr<T> >;//(C++11 起) 
template< class T > struct owner_less<std::weak_ptr<T> >;//(C++11 起) 
template<> struct owner_less<void>;// (C++17 起,类模板特化,为共享指针和弱指针提供混合类型的、基于拥有者的顺序的函数对象,无关乎被指向的类型) 

        owner_less是一个结构体模板,此函数对象提供基于拥有者(不同于基于值)的, std::weak_ptr 和 std::shared_ptr 两者的混合类型序。顺序满足二个智能指针比较相等,若且唯若它们均为空或共享所有权,即使由 get() 获得的裸指针值相异(例如因为它们指向同一对象中的不同子对象)。

//在以 std::shared_ptr 或 std::weak_ptr 为关键建立关联容器,即
std::map<std::shared_ptr<T>, U, std::owner_less<std::shared_ptr<T> > >
//或
std::map<std::weak_ptr<T>, U, std::owner_less<std::weak_ptr<T> > > //最好使用此类模板。
//默认的 operator< 不为弱指针定义,并且可能错误地认为同一对象的二个共享指针不等价。

        owner_less类功能简述:

成员函数
operator()   用基于拥有者的语义比较其参数(函数) 

所有模板特化的成员:
bool operator()( const std::shared_ptr<T>& lhs,const std::weak_ptr<T>& rhs ) const noexcept;//(C++11 起) 
bool operator()( const std::weak_ptr<T>& lhs,const std::shared_ptr<T>& rhs ) const noexcept;//(C++11 起) 

//不同特化类型比较时,用基于拥有者的语义比较 lhs 与 rhs 。等效于调用 lhs.owner_before(rhs) 。顺序是严格弱序关系。
//lhs 与 rhs 相等,当且仅当它们均为空或共享所有权。
//参数,lhs, rhs - 要比较的共享所有权指针 
//返回值,若按基于拥有者的顺序确定 lhs 小于 rhs ,则为 true 。

仅为 owner_less<shared_ptr<T>> 模板特化的成员:
bool operator()( const std::shared_ptr<T>& lhs,const std::shared_ptr<T>& rhs ) const noexcept;//(C++11 起) 
   
仅为 owner_less<weak_ptr<T>> 模板特化的成员:
bool operator()( const std::weak_ptr<T>& lhs, const std::weak_ptr<T>& rhs ) const noexcept;// (C++11 起)    

        4.2 enable_shared_from_this类

        enable_shared_from_this类声明如下:

//c++11起
template< class T > class enable_shared_from_this;

        std::enable_shared_from_this 能让其一个对象(假设其名为 t ,且已被一个 std::shared_ptr 对象 pt 管理)安全地生成其他额外的 std::shared_ptr 实例(假设名为 pt1, pt2, ... ) ,它们与 pt 共享对象 t 的所有权。若一个类 T 继承 std::enable_shared_from_this<T> ,则会为该类 T 提供成员函数: shared_from_this 。 当 T 类型对象 t 被一个为名为 pt 的 std::shared_ptr<T> 类对象管理时,调用 T::shared_from_this 成员函数,将会返回一个新的 std::shared_ptr<T> 对象,它与 pt 共享 t 的所有权。

        enable_shared_from_this类功能简述:

成员函数
(构造函数)  构造 enable_shared_from_this 对象,protected
(析构函数)  销毁 enable_shared_from_this 对象,protected
operator=  返回到 this 的引用,protected 
shared_from_this  返回共享 *this 所有权的 shared_ptr,public
weak_from_this    (C++17),返回共享 *this 所有权的 weak_ptr,public

成员对象
weak_this  (C++17)追踪 *this 的首个共享占有者的控制块的 std::weak_ptr 对象,private

        enable_shared_from_this内部保存着一个对 this 的弱引用(例如 std::weak_ptr )。 std::shared_ptr 的构造函数检测无歧义且可访问的 (C++17 起) enable_shared_from_this 基类,并且若内部存储的弱引用未为生存的 std::shared_ptr 占有,则 (C++17 起)赋值新建的 std::shared_ptr 为内部存储的弱引用。为已为另一 std::shared_ptr 所管理的对象构造一个 std::shared_ptr ,将不会考虑内部存储的弱引用,从而将导致未定义行为。

        enable_shared_from_this 提供安全的替用方案,以替代 std::shared_ptr<T>(this) 这种不安全的的表达式:

#ifndef _ENABLE_SHARED_TEST_H_
#define _ENABLE_SHARED_TEST_H_

#include <memory>
#include <iostream>
 
struct Good: std::enable_shared_from_this<Good> // 注意:继承
{
    std::shared_ptr<Good> getptr() {
        return shared_from_this();
    }
};
 
struct Bad
{
    // 错误写法:用不安全的表达式试图获得 this 的 shared_ptr 对象
    std::shared_ptr<Bad> getptr() {
        return std::shared_ptr<Bad>(this);
    }
    ~Bad() { std::cout << "Bad::~Bad() called\n"; }
};
 
void func9(void)
{
    // 正确的示例:两个 shared_ptr 对象将会共享同一对象
    std::shared_ptr<Good> gp1 = std::make_shared<Good>();
    std::shared_ptr<Good> gp2 = gp1->getptr();
    std::cout << "gp2.use_count() = " << gp2.use_count() << '\n';
 
    // 错误的使用示例:调用 shared_from_this 但其没有被 std::shared_ptr 占有
    try {
        Good not_so_good;
        std::shared_ptr<Good> gp1 = not_so_good.getptr();
    } catch(std::bad_weak_ptr& e) {
        // C++17 前为未定义行为; C++17 起抛出 std::bad_weak_ptr 异常
        std::cout << e.what() << '\n';    
    }
 
    // 错误的示例,每个 shared_ptr 都认为自己是对象仅有的所有者
    std::shared_ptr<Bad> bp1 = std::make_shared<Bad>();
    std::shared_ptr<Bad> bp2 = bp1->getptr();
    std::cout << "bp2.use_count() = " << bp2.use_count() << '\n';
} // UB : Bad 对象将会被删除两次

#endif
//out log
gp2.use_count() = 2
bad_weak_ptr
bp2.use_count() = 1
Bad::~Bad() called
Bad::~Bad() called

        4.3 bad_weak_ptr类

        bad_weak_ptr类是 std::shared_ptr 以 std::weak_ptr 为参数的构造函数,在 std::weak_ptr 指代已被删除的对象时,作为异常抛出的对象类型,定义在头文件 <memory>:

//c++11起,继承自 std::exception
class bad_weak_ptr;

        bad_weak_ptr类功能:

成员函数
(构造函数)  构造新的 bad_weak_ptr 对象,public 
bad_weak_ptr() noexcept;
bad_weak_ptr( const bad_weak_ptr& other ) noexcept; 

operator=  替换 bad_weak_ptr 对象,public 
bad_weak_ptr& operator=( const bad_weak_ptr& other ) noexcept;

what       返回解释字符串,public
virtual const char* what() const noexcept;

        在 std::weak_ptr 指代已被删除的对象时,作为异常抛出的对象类型示例:

#ifndef _BAD_WEAK_PTR_H_
#define _BAD_WEAK_PTR_H_

#include <memory>
#include <iostream>

void func10(void)
{
    std::shared_ptr<int> p1(new int(42));
    std::weak_ptr<int> wp(p1);
    p1.reset();
    try {
        std::shared_ptr<int> p2(wp);
    } catch(const std::bad_weak_ptr& e) {
        std::cout << e.what() << '\n';
    }
}

#endif //_BAD_WEAK_PTR_H_
//out log
bad_weak_ptr

        4.4 default_delete类

        std::default_delete 是不指定删除器时 std::unique_ptr 所用的默认删除策略,声明如下:

//C++11 起
template< class T > struct default_delete;
template< class T > struct default_delete<T[]>;

        default_delete 的特化在典型实现上为空类,并且用于空基类优化。

  • 非特化的 default_delete 用 delete 解分配单个对象的内存。
  • 亦为提供数组类型的使用 delete[] 的部分特化。

        default_delete类功能函数:

(构造函数)  构造 default_delete 对象,public
constexpr default_delete() noexcept = default;  
template <class U> default_delete( const default_delete<U>& d ) noexcept;//仅为初等 default_delete 模板的成员)
template<class U> default_delete( const default_delete<U[]>& d ) noexcept;//仅为 default_delete<T[]> 模板特化的成员)

operator()  删除对象或数组,public
void operator()(T* ptr) const;    //仅为初等 default_delete 模板的成员
template <class U> void operator()(U* ptr) const;//仅为 default_delete<T[]> 模板特化的成员,在 ptr 上调用 delete[] 

/*在代码中调用 operator(),类型必须完整。一些实现中用 static_assert 确保如此。此要求的原因,是 C++ 中若完整类类型拥有非平凡析构函数或解分配函数,则在不完整类型上调用 delete 是未定义行为,因为编译器无法得知这种函数是否存在且必须被调用。*/

        不指定删除器时 std::unique_ptr 调用std::default_delete 删除策略示例:

#ifndef _DEFAULT_DELETE_TEST_H_
#define _DEFAULT_DELETE_TEST_H_

#include <memory>
#include <vector>
#include <algorithm>
 
void func11(void)
{
   {
//        std::shared_ptr<int> shared_bad(new int[10]);
   } // 析构函数调用 delete ,未定义行为
 
    {
        std::shared_ptr<int> shared_good(new int[10], std::default_delete<int[]>());
    } // 析构函数调用 delete[] , ok
 
    {
        std::unique_ptr<int> ptr(new int(5));
    } // unique_ptr<int> 使用 default_delete<int>
 
    {
        std::unique_ptr<int[]> ptr(new int[10]);
    } // unique_ptr<int[]> 使用 default_delete<int[]>
 
    // default_delete 能用于需要删除用函数对象的任何场所
    std::vector<int*> v;
    for(int n = 0; n < 100; ++n)
        v.push_back(new int(n));
    std::for_each(v.begin(), v.end(), std::default_delete<int>());
}

#endif //_DEFAULT_DELETE_TEST_H_

五、智能指针不是灵丹妙药

        5.1 何时使用智能指针

        有三种典型的情况适合使用智能指针:

  • 资源所有权的共享

        共享所有权是指两个或多个对象需要同时使用第某个对象的情况。这个对象应该如何(或者说何时)被释放?为了确保释放的时机是正确的,每个使用这个共享资源的对象必须互相知道对方,才能准确掌握资源的释放时间。从设计或维护的观点来看,这种耦合是不可行的。更好的方法是让这些资源所有者将资源的生存期管理责任委派给一个智能指针。当没有共享者存在时,智能指针就可以安全地释放这个资源了。

  • 要写异常安全的代码时

        异常安全,简单地说就是在异常抛出时没有资源泄漏并保证程序状态的一致性。如果一个对象是动态分配的,当异常抛出时它不会被删除。由于栈展开以及指针离开作用域,资源可以会泄漏直至程序结束(即使是程序结束时的资源回收也不是语言所保证的)。不仅可能程序会由于内存泄漏而耗尽资源,程序的状态也可能变得混乱。智能指针可以自动地为你释放这些资源,即使是在异常发生的情况下。

  • 避免常见的错误,如资源泄漏

          避免常见的错误。忘记调用 delete 的错误,尤其是在复杂业务逻辑情况下。一个智能指针不关心程序中的控制路径;它只关心在它所指向的对象的生存期结束时删除它。使用智能指针,你不再需要知道何时删除对象。并且,智能指针隐藏了释放资源的细节,因此使用者不需要知道是否要调用 delete, 有些特殊的清除函数并不总是删除资源的。

        5.2 有些时候要避免智能指针

        使用裸指针来写异常安全和无错误的代码是很复杂的。使用智能指针来自动地把动态分配对象的生存期限制在一个明确的范围之内,是解决这种问题的一个有效的方法,并且提高了代码的可读性、可维护性和质量。

        其一,智能指针并不智能,还是需要人去精心控制,对于一些大量对象存储以及循环遍历的应用来说,智能指针带来的性能损耗显然易见的,且看下面的例子:

#ifndef _RUN_TEST_H_
#define _RUN_TEST_H_

#include <vector>
#include <iostream>
#include <memory>
#include <time.h>

class AClass
{
private:
    int val;
public:
    AClass(const int &val_);
    ~AClass();
    void setVal(const int &val_);
};

AClass::AClass(const int &val_) : val(val_){ }

AClass::~AClass(){ }

void AClass::setVal(const int &val_)
{
    val = val_;
}

const int VSIZE = 10000000;
void func12(void)
{
    std::vector<AClass*> vecs;
    for (size_t i = 0; i < VSIZE; i++)
    {
        vecs.push_back(new AClass(i%10));
    }
    for (size_t i = 0; i < VSIZE; i++)
    {
        vecs[i]->setVal(i%100);
    }
    for (size_t i = 0; i < VSIZE; i++)
    {
        delete vecs[i];
        vecs[i] = NULL;
    }
    vecs.clear();
}

void func13(void)
{
    std::vector<std::unique_ptr<AClass> > vecs;
    for (size_t i = 0; i < VSIZE; i++)
    {
        vecs.push_back(std::unique_ptr<AClass>(new AClass(i%10)));
    }
    for (size_t i = 0; i < VSIZE; i++)
    {
        vecs[i]->setVal(i%100);
    }   
}

#endif
//main.cpp
    std::cout << "clock() 1= " << clock() << "\n";
    func12();
    std::cout << "clock() 2= " << clock() << "\n";
    func13();
    std::cout << "clock() 3= " << clock() << "\n";
//out log
clock() 1= 0
clock() 2= 1355
clock() 3= 7041

        win和linux输出:

         其二,避免滥用智能指针。对于一些简单、直观、易懂的业务逻辑实现,引入智能指针反而影响其可阅读性、执行效率,其实开发者如果能有效进行内存管理,就不必要引入智能指针。

        其三,各家使用智能指针差别挺大的,智能指针的传染性很大,涉及到跨新老款库、第三方库等归一及兼容问题时,要替换的话,就要替换所有的指针,风险很大、工程很大。

        5.3 c++智能指针还不成熟稳定

        智能指针在C++各个版本中不断地又增删,说明C++标准委员会对于智能指针在c++的引用也是不断权衡中,像c++14、 17、 20、 23都对智能指针的成员变量及成员函数作出了调整,c++23还增加了智能指针适配器。

智能指针适配器(C++23)
out_ptr_t      与外来指针设置器交互,并在析构时重设智能指针(类模板) 
out_ptr        以关联的智能指针和重设参数创建 out_ptr_t (函数模板)  
inout_ptr_t    与外来指针设置器交互,从智能指针获得初始指针值,并在析构时重设它(类模板) 
inout_ptr      以关联的智能指针和重设参数创建 inout_ptr_t(函数模板) 

         使用智能指针,优先考虑使用unique_ptr,因为它没有开销,相对成熟,是用来替代旧的auto_ptr的,当使用shared_ptr时,则要问题自己,是否非他不可,采用它是否能带来实际性改善,否则不建议使用。

六、附录-各智能指针类详细声明代码

        6.1标准库中类模板 std::unique_ptr的详细声明

namespace std {
  template<class T, class D = default_delete<T>> class unique_ptr {
  public:
    using pointer      = /* 见描述 */;
    using element_type = T;
    using deleter_type = D;
 
    // 构造函数
    constexpr unique_ptr() noexcept;
    explicit unique_ptr(pointer p) noexcept;
    unique_ptr(pointer p, /* 见描述 */ d1) noexcept;
    unique_ptr(pointer p, /* 见描述 */ d2) noexcept;
    unique_ptr(unique_ptr&& u) noexcept;
    constexpr unique_ptr(nullptr_t) noexcept;
    template<class U, class E>
      unique_ptr(unique_ptr<U, E>&& u) noexcept;
 
    // 析构函数
    ~unique_ptr();
 
    // 赋值
    unique_ptr& operator=(unique_ptr&& u) noexcept;
    template<class U, class E>
      unique_ptr& operator=(unique_ptr<U, E>&& u) noexcept;
    unique_ptr& operator=(nullptr_t) noexcept;
 
    // 观察器
    add_lvalue_reference_t<T> operator*() const noexcept(/* 见描述 */);
    pointer operator->() const noexcept;
    pointer get() const noexcept;
    deleter_type& get_deleter() noexcept;
    const deleter_type& get_deleter() const noexcept;
    explicit operator bool() const noexcept;
 
    // 修改器
    pointer release() noexcept;
    void reset(pointer p = pointer()) noexcept;
    void swap(unique_ptr& u) noexcept;
 
    // 禁用从左值复制
    unique_ptr(const unique_ptr&) = delete;
    unique_ptr& operator=(const unique_ptr&) = delete;
  };
 
  template<class T, class D> class unique_ptr<T[], D> {
  public:
    using pointer      = /* 见描述 */;
    using element_type = T;
    using deleter_type = D;
 
    // 构造函数
    constexpr unique_ptr() noexcept;
    template<class U> explicit unique_ptr(U p) noexcept;
    template<class U> unique_ptr(U p, /* 见描述 */ d) noexcept;
    template<class U> unique_ptr(U p, /* 见描述 */ d) noexcept;
    unique_ptr(unique_ptr&& u) noexcept;
    template<class U, class E>
      unique_ptr(unique_ptr<U, E>&& u) noexcept;
    constexpr unique_ptr(nullptr_t) noexcept;
 
    // 析构函数
    ~unique_ptr();
 
    // 赋值
    unique_ptr& operator=(unique_ptr&& u) noexcept;
    template<class U, class E>
      unique_ptr& operator=(unique_ptr<U, E>&& u) noexcept;
    unique_ptr& operator=(nullptr_t) noexcept;
 
    // 观察器
    T& operator[](size_t i) const;
    pointer get() const noexcept;
    deleter_type& get_deleter() noexcept;
    const deleter_type& get_deleter() const noexcept;
    explicit operator bool() const noexcept;
 
    // 修改器
    pointer release() noexcept;
    template<class U> void reset(U p) noexcept;
    void reset(nullptr_t = nullptr) noexcept;
    void swap(unique_ptr& u) noexcept;
 
    // 禁用从左值复制
    unique_ptr(const unique_ptr&) = delete;
    unique_ptr& operator=(const unique_ptr&) = delete;
  };
}

        6.2 类模板 std::shared_ptr

namespace std {
  template<class T> class shared_ptr {
  public:
    using element_type = remove_extent_t<T>;
    using weak_type    = weak_ptr<T>;
 
    // 构造函数
    constexpr shared_ptr() noexcept;
    constexpr shared_ptr(nullptr_t) noexcept : shared_ptr() { }
    template<class Y>
      explicit shared_ptr(Y* p);
    template<class Y, class D>
      shared_ptr(Y* p, D d);
    template<class Y, class D, class A>
      shared_ptr(Y* p, D d, A a);
    template<class D>
      shared_ptr(nullptr_t p, D d);
    template<class D, class A>
      shared_ptr(nullptr_t p, D d, A a);
    template<class Y>
      shared_ptr(const shared_ptr<Y>& r, element_type* p) noexcept;
    template<class Y>
      shared_ptr(shared_ptr<Y>&& r, element_type* p) noexcept;
    shared_ptr(const shared_ptr& r) noexcept;
    template<class Y>
      shared_ptr(const shared_ptr<Y>& r) noexcept;
    shared_ptr(shared_ptr&& r) noexcept;
    template<class Y>
      shared_ptr(shared_ptr<Y>&& r) noexcept;
    template<class Y>
      explicit shared_ptr(const weak_ptr<Y>& r);
    template<class Y, class D>
      shared_ptr(unique_ptr<Y, D>&& r);
 
    // 析构函数
    ~shared_ptr();
 
    // 赋值
    shared_ptr& operator=(const shared_ptr& r) noexcept;
    template<class Y>
      shared_ptr& operator=(const shared_ptr<Y>& r) noexcept;
    shared_ptr& operator=(shared_ptr&& r) noexcept;
    template<class Y>
      shared_ptr& operator=(shared_ptr<Y>&& r) noexcept;
    template<class Y, class D>
      shared_ptr& operator=(unique_ptr<Y, D>&& r);
 
    // 修改器
    void swap(shared_ptr& r) noexcept;
    void reset() noexcept;
    template<class Y>
      void reset(Y* p);
    template<class Y, class D>
      void reset(Y* p, D d);
    template<class Y, class D, class A>
      void reset(Y* p, D d, A a);
 
    // 观察器
    element_type* get() const noexcept;
    T& operator*() const noexcept;
    T* operator->() const noexcept;
    element_type& operator[](ptrdiff_t i) const;
    long use_count() const noexcept;
    explicit operator bool() const noexcept;
    template<class U>
      bool owner_before(const shared_ptr<U>& b) const noexcept;
    template<class U>
      bool owner_before(const weak_ptr<U>& b) const noexcept;
  };
 
  template<class T>
    shared_ptr(weak_ptr<T>) -> shared_ptr<T>;
  template<class T, class D>
    shared_ptr(unique_ptr<T, D>) -> shared_ptr<T>;
}

        6.3 类模板 std::weak_ptr

namespace std {
  template<class T> class weak_ptr {
  public:
    using element_type = remove_extent_t<T>;
 
    // 构造函数
    constexpr weak_ptr() noexcept;
    template<class Y>
      weak_ptr(const shared_ptr<Y>& r) noexcept;
    weak_ptr(const weak_ptr& r) noexcept;
    template<class Y>
      weak_ptr(const weak_ptr<Y>& r) noexcept;
    weak_ptr(weak_ptr&& r) noexcept;
    template<class Y>
      weak_ptr(weak_ptr<Y>&& r) noexcept;
 
    // 析构函数
    ~weak_ptr();
 
    // 赋值
    weak_ptr& operator=(const weak_ptr& r) noexcept;
    template<class Y>
      weak_ptr& operator=(const weak_ptr<Y>& r) noexcept;
    template<class Y>
      weak_ptr& operator=(const shared_ptr<Y>& r) noexcept;
    weak_ptr& operator=(weak_ptr&& r) noexcept;
    template<class Y>
      weak_ptr& operator=(weak_ptr<Y>&& r) noexcept;
 
    // 修改器
    void swap(weak_ptr& r) noexcept;
    void reset() noexcept;
 
    // 观察器
    long use_count() const noexcept;
    bool expired() const noexcept;
    shared_ptr<T> lock() const noexcept;
    template<class U>
      bool owner_before(const shared_ptr<U>& b) const noexcept;
    template<class U>
      bool owner_before(const weak_ptr<U>& b) const noexcept;
  };
 
  template<class T>
    weak_ptr(shared_ptr<T>) -> weak_ptr<T>;
}

        6.4 四个辅助类

namespace std {
  template<class T = void> struct owner_less;
 
  template<class T> struct owner_less<shared_ptr<T>> {
    bool operator()(const shared_ptr<T>&, const shared_ptr<T>&) const noexcept;
    bool operator()(const shared_ptr<T>&, const weak_ptr<T>&) const noexcept;
    bool operator()(const weak_ptr<T>&, const shared_ptr<T>&) const noexcept;
  };
 
  template<class T> struct owner_less<weak_ptr<T>> {
    bool operator()(const weak_ptr<T>&, const weak_ptr<T>&) const noexcept;
    bool operator()(const shared_ptr<T>&, const weak_ptr<T>&) const noexcept;
    bool operator()(const weak_ptr<T>&, const shared_ptr<T>&) const noexcept;
  };
 
  template<> struct owner_less<void> {
    template<class T, class U>
      bool operator()(const shared_ptr<T>&, const shared_ptr<U>&) const noexcept;
    template<class T, class U>
      bool operator()(const shared_ptr<T>&, const weak_ptr<U>&) const noexcept;
    template<class T, class U>
      bool operator()(const weak_ptr<T>&, const shared_ptr<U>&) const noexcept;
    template<class T, class U>
      bool operator()(const weak_ptr<T>&, const weak_ptr<U>&) const noexcept;
 
    using is_transparent = /* 未指明 */;
  };
}

namespace std {
  template<class T> class enable_shared_from_this {
  protected:
    constexpr enable_shared_from_this() noexcept;
    enable_shared_from_this(const enable_shared_from_this&) noexcept;
    enable_shared_from_this& operator=(const enable_shared_from_this&) noexcept;
    ~enable_shared_from_this();
 
  public:
    shared_ptr<T> shared_from_this();
    shared_ptr<T const> shared_from_this() const;
    weak_ptr<T> weak_from_this() noexcept;
    weak_ptr<T const> weak_from_this() const noexcept;
 
  private:
    mutable weak_ptr<T> weak_this;  // 仅用于阐释
  };
}

namespace std {
  class bad_weak_ptr : public exception {
  public:
    bad_weak_ptr() noexcept;
  };
}

namespace std {
  template<class T> struct default_delete {
    constexpr default_delete() noexcept = default;
    template<class U> default_delete(const default_delete<U>&) noexcept;
    void operator()(T*) const;
  };
 
  template<class T> struct default_delete<T[]> {
    constexpr default_delete() noexcept = default;
    template<class U> default_delete(const default_delete<U[]>&) noexcept;
    template<class U> void operator()(U* ptr) const;
  };
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/375538.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Idea启动多个SpringBoot项目的3种方案

​ 悟纤&#xff1a;师傅&#xff0c;你最近是哪去了&#xff0c;这上班都快1个月了&#xff0c;都没见你踪影&#xff0c;你这是上哪里去放纵去了吗&#xff1f; 师傅&#xff1a;徒儿&#xff0c;你说的这是啥话&#xff0c;是放松&#xff0c;不是放纵&#xff0c;为师严重…

axicom的测试文档

目录&#xff09;SQLpython开放性业务题&#xff08;二选一&#xff09;完整代码SQL 问题描述 SQL&#xff0c; 请根据前一周各产品的总GMV将其分成五类&#xff1a;GMV Top 20%、20%-40%&#xff0c;40%-60%&#xff0c;60%-80%以及Bottom 20%的产品组&#xff0c;请计算这五…

【Java】创建多线程的四种方式

一、方式1&#xff1a;继承Thread类 步骤&#xff1a; 创建一个继承于Thread类的子类重写Thread类的run()方法 ----> 此线程执行的操作声明在方法体中创建当前Thread子类的对象通过实例对象调用start()方法&#xff0c;启动线程 ----> Java虚拟机会调用run()方法 注意…

QuickHMI Hawk R3 Crack

基于网络的 SCADA / HMI 系统 QuickHMI Hawk R3 QuickHMI是一个 100% 基于网络的SCADA/HMI 系统。 得益于HTML5、SVG和Javascript等现代网络技术&#xff0c;可视化可以在任何当前浏览器和设备中显示。作为浏览器的替代品&#xff0c;可以使用“独立查看器”和移动应用程序。 Q…

react react-redux学习记录

react react-redux学习记录1.原理2.怎么用呢2.1 容器组件2.2UI组件2.3 App.jsx3.简化3.1简写mapDispatch3.2 Provider组件的使用3.3整合UI组件和容器组件1.原理 UI组件:不能使用任何redux的api&#xff0c;只负责页面的呈现、交互等。 容器组件&#xff1a;负责和redux通信&…

Orcad原理图放置辅助线的方法

Orcad原理图放置辅助线的方法 设计当中&#xff0c;可以通过放置辅助线来标识信号方向或者对功能模块进行分块标识。 1&#xff09;执行菜单命令“Place-Line”&#xff08;快捷键“ShiftL”&#xff09;&#xff0c;激活放置状态。 2&#xff09;在一个合适的位置单击鼠标左键…

零基础该如何转行Python工程师?学习路线是什么?

最近1年的主要学习时间&#xff0c;都投资到了 python 数据分析和数据挖掘上面来了&#xff0c;虽然经验并不是十分丰富&#xff0c;但希望也能把自己的经验分享下&#xff0c;最近也好多朋友给我留言&#xff0c;和我聊天&#xff0c;问我python该如何学习&#xff0c;才能少走…

字节前端必会面试题(持续更新中)

事件传播机制&#xff08;事件流&#xff09; 冒泡和捕获 谈一谈HTTP数据传输 大概遇到的情况就分为定长数据 与 不定长数据的处理吧。 定长数据 对于定长的数据包而言&#xff0c;发送端在发送数据的过程中&#xff0c;需要设置Content-Length,来指明发送数据的长度。 当…

前端面试题——性能优化 高频

目录 一、CDN的概念 二、CDN的原理 三、懒加载的实现原理 四、对节流与防抖的理解 五、实现节流函数和防抖函数 六、回流与重绘的概念及触发条件 七、如何避免回流与重绘&#xff1f; 八、如何对项目中的图片进行优化&#xff1f; 九、如何⽤webpack来优化前端性能&…

Python 简单可变、复杂可变、简单不可变、复杂不可变类型的copy、deepcopy的行为

copy模块&#xff1a;copy&#xff1a;浅拷贝deepcopy&#xff1a;深拷贝简单可变类型、复杂可变的copy()、deepcopy()&#xff1a;简单不可变、复杂不可变类型的copy()、deepcopy()&#xff1a;结论&#xff1a;对于简单类型的可变类型copy是深拷贝&#xff0c;改变了该拷贝变…

1.FFmpeg-音视频基础

专栏介绍基于最新的FFmpeg5.1.2版本讲解学习, 跟随博主一起学习ffmpeg: 本专栏学习流程为: FFmpeg安装、

Spring之AOP理解及使用

文章目录AOP是什么AOPSpring的通知类型1.Before通知2. AfterReturning通知3.AfterThrowing通知4. After通知5. Around通知动态代理JDK动态代理CGLib动态代理动态代理的代码展示AOP使用切面类的配置最后大家好&#xff0c;我是Leo&#xff01;今天给大家带来的是关于Spring AOP的…

jsp学生成果管理系Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 学生成果管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0…

分布式理论-学习笔记

1 分布式概述 分布式系统是计算机程序的集合&#xff0c;这些程序利用跨多个独立计算节点的计算资源来实现共同的目标。可以分为分布式计算、分布式存储、分布式数据库等。 2 系统模型 2.1 故障模型 Byzantine failure:节点可以任意篡改发送给其他节点的数据ADB:Byzantine fa…

Stacked hourglass networks for human pose estimation代码学习

Stacked hourglass networks for human pose estimation https://github.com/princeton-vl/pytorch_stacked_hourglass 这是一个用于人体姿态估计的模型&#xff0c;只能检测单个人 作者通过重复的bottom-up&#xff08;高分辨率->低分辨率&#xff09;和top-down&#xff0…

乐友商城学习笔记(十五)

无状态登陆原理 在服务器端保存session 无状态不需要session&#xff0c;把登陆状态保存在cookie中 jwtrsa token&#xff1a;登陆时&#xff0c; jwt oath2 jwt&#xff1a;头信息&#xff08;jwt&#xff09; 载荷&#xff08;用户信息&#xff0c;签发人&#xff0c;签发时…

设备驱动模型--存储技术原理分析笔记 基于2.6.43内核

本文为读书笔记&#xff0c;详细内容参考《存储原理技术分析》1- 驱动模型2- 总线类型2.1- 重要数据结构总线bus_type 和 bus_type_private 互相可以找到对方struct bus_type {const char *name;struct bus_attribute *bus_attrs;struct device_attribute *dev_attrs;s…

2023软件测试工程师全新技术栈,吃透这些,起薪就是25k~

相信每个准备软件测试面试的同学&#xff0c;不管你是大学刚毕业&#xff0c;满心憧憬着进入公司实习、非计算机行业转行软件测试、自学测试就业还是培训后就业&#xff0c;都会面临着众多的疑问和不解&#xff0c;那就是该怎么走出着第一步&#xff0c;今天本文一次性告诉你&a…

MK60DX256VLQ10(256KB)MK60DN256VLQ10 Kinetis K60 MCU FLASH

MK60DX256VLQ10(256KB)MK60DN256VLQ10 Kinetis K60 MCU 32BIT 256KB FLASH 144LQFP【说明】Kinetis K6x MCU系列是一个可扩展的组合&#xff0c;具有不同级别的集成&#xff0c;提供丰富的模拟、通信、定时和控制外设套件&#xff0c;以适应广泛的需求。应用楼宇自动化控制器人…

ARM中的寄存器

ARM工作模式 ARM有8个基本的工作模式 User 非特权模式&#xff0c;一般在执行上层的应用程序时ARM处于该模式FIQ 当一个高优先级中断产生后ARM将进入这种模式IRQ 当一个低优先级中断产生后ARM将进入这种模式SVC 当复位或执行软中断指令后ARM将进入这种模式Abort 当产生存取异常…