c++ 自学笔记 --- 智能指针

智能指针

普通指针的不足

  • newnew[] 的内存需要用 deletedelete[] 释放。
  • 程序员的主观失误,忘了或漏了释放。
  • 程序员也不确定何时释放。

普通指针的释放

  • 类内的指针,在析构函数中释放。
  • C++ 内置数据类型,如何释放? 手动 delete
  • new 出来的类,本身如何释放?

智能指针的设计思路

  • 智能指针是模板类,在栈上创建智能指针对象。
  • 把普通指针交给智能指针对象。
  • 智能指针对象过期时,调用析构函数释放普通指针的内存。

智能指针的类型

  • unique_ptr: C++ 11标准。
  • shared_ptr: C++ 11标准。
  • weak_ptr: C++ 11标准。
  • auto_ptr: C++98 标准,C++11 弃用,C++17 移除。
  • scoped_ptr: Boost中,STL未引入。

智能指针 - unique_ptr

从 C++ 11 开始,定义在 中,独享它指向的对象,也就是说,同时只有一个 unique_ptr 指向同一个对象,当这个 unique_ptr 被销毁时,指向的对象也随之被销毁。

  • 初始化

    • 分配内存并初始化:
      std::unique_ptr pu(new City("Shenzhen"));
      
    • std::make_unique 初始化(C++14 标准):
      std::unique_ptr pu = std::make_unique("Shenzhen");
      
    • 用已存在的地址初始化:
      City *p = new City("Shenzhen");
      std::unique_ptr pu(p); 
      
  • 使用方法

    • 智能指针重载了 *-> 操作符,可以像使用指针一样使用 unique_ptr
      std::unique_ptr pu3 = std::make_unique("Shenzhen");
      std::cout << "m_name = " << (*pu3).m_name << std::endl;
      std::cout << "m_name = " << pu3->m_name << std::endl;
      
    • 不支持普通的拷贝和赋值。
      std::unique_ptr pu1(new City("Shenzhen"));
      City *p = new City("Shenzhen");
      
      // 错误,不能把普通指针直接赋值给智能指针。
      std::unique_ptr pu2 = p;
      // 错误,不能把普通指针直接赋值给智能指针。
      std::unique_ptr pu3 = new City("Shenzhen"); 
      // 错误,不能用其它的unique_ptr拷贝构造。
      std::unique_ptr pu2 = pu1;
      
      std::unique_ptr pu3;
      pu3 = pu1;// 错误,不能用“=”对unique_ptr进行赋值。
      
    • 不要用同一个裸指针初始化多个 unique_ptr
      City *p = new City("Shenzhen");
      std::unique_ptr pu1(p);
      // std::unique_ptr pu2(p); // 错误,不能把普通指针传给多个智能指针。
      // std::unique_ptr pu3(p); // 错误,不能把普通指针传给多个智能指针
      
    • get() 方法返回裸指针。
      City *p = new City("Shenzhen");
      std::unique_ptr pu(p);
      
      std::cout << "p       : " << p << std::endl;
      std::cout << "pu.get(): " << pu.get() <
    • 不要用 unique_ptr 管理不是 new 分配的内存。
  • 函数参数中使用unique_ptr

    • 传引用。
    • 传值(不能直接传值,因为unique_ptr没有拷贝构造函数,可以借助std::move())。
    • 传裸指针。
  • 不支持指针的运算(+-++--

  • 其它技巧和陷阱

    • unique_ptr 赋值给另一个时,如果源 unique_ptr 是一个临时右值,编译器允许这样做; 如果源 unique_ptr 将存在一段时间,编译器禁止这样做。一般用于函数返回值。
    • nullptrunique_ptr 赋值将释放对象,空的 unique_ptr == nullptr
    • release() 释放对原始指针的控制权,将 unique_ptr 置为空,返回裸指针。可用于把 unique_ptr 传递给子函数,子函数将负责释放对象。
    • std::move() 可以转移对原始指针的控制权。可用于把 unique_ptr 传递给子函数,子函数形参也是 unique_ptr
    • reset() 释放对象
      pu1.reset(); // 重置指向的原始指针,默认置为nullptr。
      pu2.reset(nullptr);
      pu3.reset(new City("Tianjing")); // 释放旧的原始指针,指向新的普通指针。
      
    • swap() 交换两个 unique_ptr 的控制权。
      std::unique_ptr pu1(new City("ShenZhen"));
      std::unique_ptr pu2(new City("Beijing"));
      pu1.swap(pu2);
      
    • unique_ptr 也可像普通指针那样,当指向一个类继承体系的基类对象时,也具有多态性质,如同使用裸指针管理基类对象和派生类对象那样。
    • unique_ptr 不是绝对安全。如程序用 exit() 退出,全局 unique_ptr 可自动释放,但局部非 static unique_ptr 无法释放。 操作空的 unique_ptr 也有可能出现野指针的情况。
    • unique_ptr 提供支持数组的具体化版本,数组版本的 unique_ptr ,重载操作符 [],操作符 [] 返回的是引用,可以作为左值使用。

智能指针 - shared_ptr

shared_ptr 共享它指向的对象,多个 shared_ptr 可指向(关联)相同对象,在内部采用计数机制来实现。当新的 shared_ptr 与对象关联时,引用计数增加 1shared_ptr 超出作用域时,引用计数减 1。当引用计数变为 0 时,则表示没有shared_ptr 与对象关联,则释放该对象。shared_ptr 的构造函数也是 explicit,但没有删除拷贝构造函数和赋值函数。

  • 初始化

    • 分配内存并初始化:

      City *p = new City("Shenzhen");
      std::shared_ptr p0(p);
      
    • C++11 标准,效率更高。

      std::shared_ptr p0(new City("Shenzhen"));
      
    • 用已存在的地址初始化:

      std::shared_ptr p0 = std::make_shared("Shenzhen");
      
    • 用已存在的 shared_ptr 初始化,计数加1。

      std::shared_ptr p0(new City("Shenzhen"));
      std::shared_ptr p1(p0);
      std::shared_ptr p2 = p0;
      
  • 使用方法

    • 智能指针重载了*->操作符,可像使用指针一样使用 shared_ptr
    • use_count() 方法返回引用计数器的值。
    • unique() 方法,如果 use_count()1,返回 true, 否则返回false
    • 支持普通的拷贝和赋值, 左值shared_ptr 的计数器减 1 ,右值shared_ptr计数器加 1
    • get() 方法返回裸指针。
    • 不要用同一个裸指针初始化多个 shared_ptr
    • 不要用shared_ptr 管理不是 new 分配的内存。
  • 函数参数中使用 shared_ptr

    • 传引用。
    • 传值。
    • 传裸指针。
  • 不支持指针的运算(+-++--

  • 其它技巧和陷阱

    • 拷贝构造函数和赋值函数(与 unique_ptr 不一样)。
    • nullptrshared_ptr 赋值将把计数器减 1 ,如果计数器为 0 ,将释放对象,空的 shared_ptr == nullptr
    • 没有 release() 成员函数(与 unique_ptr 不一样)。
    • std::move() 可以转移对原始指针的控制权。可将 unique_ptr 转移成 shared_ptr
    • reset() 改变与资源的关联关系。
      pp.reset(); // 解除与资源的关系,资源的引用计数器减1。
      pp.reset(new City("Shenzhen")); //解除与资源的关系,资源的引用计数减1,并关联新的资源。
      
    • swap() 交换两个 shared_ptr 的控制权。
    • shared_ptr 也可像普通指针那样,当指向一个类继承体系的基类对象时,也具有多态的性质,如同使用裸指针管理基类对象和派生类对象那样。
    • shared_ptr 不是绝对安全,如果程序中调用 exit() 退出,全局的 shared_ptr 可自动释放,但局部非 static shared_ptr 无法释放。
    • shared_ptr 提供了支持数组的具体化版本。
      数组版本的 shared_ptr ,重载了操作符 [],操作符 [] 返回的是引用,可以作为左值使用。
    • shared_ptr 线程安全性:
      shared_ptr 引用计数本身是线程安全(引用计数是原子操作)。
      多线程同时读同一 shared_ptr 对象是线程安全的。
      多线程对用一 shared_ptr 对象进行读和写,则需要加锁。
      多线程读写 shared_ptr 所指同一对象,不管是相同 shared_ptr 对象,还是不同 shared_ptr 对象,也需要加锁。
    • 如果 unique_ptr 能解决问题,就不要用 shared_ptrunique_ptr 效率更高,占用资源更少。

智能指针 - weak_ptr

  • shared_ptr 存在的问题
    shared_ptr 内部维护一个共享引用计数器,多个 shared_ptr 可指向同一资源。如果出现了循环引用情况,引用计数永远无法归 0,资源不会被释放。

  • 为什么引入 weak_ptr
    weak_ptr 为配合 shared_ptr 而引入,指向一个由 shared_ptr 管理的资源,但不影响资源生命周期。 换言之,将一 weak_ptr 绑定到一 shared_ptr 不会改变 shared_ptr 的引用计数。 不论是否有 weak_ptr 指向,如果最后一个指向资源的 shared_ptr 被销毁,资源就会被释放。 weak_ptr 更像是 shared_ptr 助手。

  • 使用方法

    • weak_ptr 没有重载 ->* 操作符,不能直接访问资源。
    • 成员函数:
      operator=(); // 把 shared_ptr 或 weak_ptr 赋值给 weak_ptr。
      expired();   // 判断它指向的资源是否已过期(过期则销毁)。
      lock();      // 返回 shared_ptr, 如果资源已过期,返回空的 shared_ptr。
      reset();     // 将当前 weak_ptr 指针置为空。
      swap();      // 交换。
      
    • weak_ptr 不控制对象生命周期,但知道对象是否还活着。
    • lock() 函数可提升为 shared_ptr
      如果对象还活着,返回有效的 shared_ptr
      如果对象已死了,提升会失败,返回一个空的 shared_ptr
    • lock() 提升行为线程安全。

智能指针 - 删除器

默认情况下,智能指针过期时,用 delete 释放原始指针。程序员可以自定义删除器,改变智能指针释放资源的行为。删除器可以是全局函数仿函数Lambda表达式,形参为原始指针。

void DeleteFunction(City *city) // 删除器,普通函数
{
    std::cout << "DeleteFunction(" << city->m_name << ")" << std::endl;
    delete city;
}

struct DeleteClass // 删除器,仿函数
{
    void operator()(City *city)
    {
        std::cout << "DeleteClass(" << city->m_name << ")" << std::endl;
        delete city;
    }
};

auto DeleteLambda = [](City *city) { // 删除器,Lambda表达式
    std::cout << "DeleteLambda(" << city->m_name << ")" << std::endl;
    delete city;
};

int main(void)
{
    // std::shared_ptr pa1(new City("Shenzhen")); // 使用缺省的删除器,即用delete关键字删除。
    // std::shared_ptr pa2(new City("Beijing"), DeleteFunction); // 删除器,普通函数。
    // std::shared_ptr pa3(new City("Shanghai"), DeleteClass()); // 删除器,仿函数。
    // std::shared_ptr pa4(new City("Guangzhou"), DeleteLambda); // 删除器,Lambda表达式。

    // std::unique_ptr pu0(new City("Shenzhen"));
    std::unique_ptr pu1(new City("Beijing"), DeleteFunction);
    // std::unique_ptr pu2(new City("Shanghai"), DeleteFunction);
    // std::unique_ptr pu3(new City("Guangzhou"), DeleteClass());
    // std::unique_ptr pu4(new City("Tianjin"), DeleteLambda);
    
    return 0;
}

输出:

jiazhao@MININT-04QBDDV:~/samples$ g++ smart_pointer_deleter.cpp && ./a.out 
City(Beijing)
DeleteFunction(Beijing)
~City(Beijing)
jiazhao@MININT-04QBDDV:~/samples$

示例代码 - unique_ptr

基于 City 类进行测试,代码如下:

#include 
#include 

class City
{
public:
    std::string m_name;
    City()
    {
        std::cout << "City()" << std::endl;
    }

    City(std::string name) : m_name(name)
    {
        std::cout << "City(" << m_name << ")" << std::endl;
    }

    ~City()
    {
        std::cout << "~City(" << m_name << ")" << std::endl;
    }
};

  • 初始化方法
int main(void)
{
    // 方法一:
    City *p = new City("Shenzhen");
    std::unique_ptr pu1(p);
    std::cout << "m_name = " << (*p).m_name << std::endl;
    std::cout << "m_name = " << p->m_name << std::endl;
    
    // 方法二:
    std::unique_ptr pu2(new City("Shenzhen"));

    // 方法三 (std::make_unique C++14 标准):
    std::unique_ptr pu3 = std::make_unique("Shenzhen");

    // 智能指针重载了 `*` 和 `->` 操作符,可以像使用指针一样使用 `unique_ptr`。
    std::cout << "m_name = " << (*pu3).m_name << std::endl;
    std::cout << "m_name = " << pu3->m_name << std::endl;

    return 0;
}

输出:

jiazhao@MININT-04QBDDV:~/samples$ g++ smart_pointer_unique_ptr.cpp && ./a.out 
City(Shenzhen)
m_name = Shenzhen
m_name = Shenzhen
City(Shenzhen)
City(Shenzhen)
m_name = Shenzhen
m_name = Shenzhen
~City(Shenzhen)
~City(Shenzhen)
~City(Shenzhen)
jiazhao@MININT-04QBDDV:~/samples$ 
  • 不支持普通的拷贝和赋值。
int main ()
{
    std::unique_ptr pu1(new City("Shenzhen"));
    City *p = new City("Beijing");

    // std::unique_ptr pu2 = p;                    // 错误,不能把普通指针直接赋值给智能指针。
    // std::unique_ptr pu3 = new City("Shenzhen"); // 错误,不能把普通指针直接赋值给智能指针。

    // std::unique_ptr pu2 = pu1;                  // 错误,不能用其它的unique_ptr拷贝构造。

    std::unique_ptr pu3;
    // pu3 = pu1;                                        // 错误,不能用“=”对unique_ptr进行赋值。

    // delete p; // 注释掉不会析构
    return 0;
}

输出:

jiazhao@MININT-04QBDDV:~/samples$ g++ smart_pointer_unique_ptr.cpp && ./a.out 
City(Shenzhen)
City(Shenzhen)
~City(Shenzhen)
jiazhao@MININT-04QBDDV:~/samples$ 
  • 不要用同一个裸指针初始化多个 unique_ptr
int main(void)
{
    City *p = new City("Shenzhen");
    std::unique_ptr pu1(p);
    std::unique_ptr pu2(p); // 错误,不能把普通指针传给多个智能指针。
    std::unique_ptr pu3(p); // 错误,不能把普通指针传给多个智能指针。
    return 0;
}

输出:

jiazhao@MININT-04QBDDV:~/samples$ g++ smart_pointer_unique_ptr.cpp && ./a.out 
City(Shenzhen)
~City(Shenzhen)
Segmentation fault
jiazhao@MININT-04QBDDV:~/samples$ 
  • get() 方法返回裸指针。
int main (void)
{
    City *p = new City("Shenzhen");
    std::unique_ptr pu(p);

    std::cout << "p       : " << p << std::endl;
    std::cout << "pu.get(): " << pu.get() << std::endl;
    std::cout << "&pu     : " << &pu << std::endl;

    return 0;
}

输出:

jiazhao@MININT-04QBDDV:~/samples$ g++ smart_pointer_unique_ptr.cpp && ./a.out 
City(Shenzhen)
p       : 0x560cb7efbeb0
pu.get(): 0x560cb7efbeb0
&pu     : 0x7ffd7495e9d0
~City(Shenzhen)
jiazhao@MININT-04QBDDV:~/samples$ 
  • 函数参数中使用unique_ptr
// 1) 传引用: BeautifulCity(pu);
void BeautifulCity1(std::unique_ptr &pp) 
{
    std::cout << "&pp, m_name: " << pp->m_name << std::endl;
}

// 2)传值: 删除了拷贝构造,不能直接传值,可用:BeautifulCity(std::move(pu));
void BeautifulCity2(std::unique_ptr pp) 
{
    std::cout << "pp,  m_name: " << pp->m_name << std::endl;
}

// 3) 传指针:BeautifulCity3(&pu)
void BeautifulCity3(std::unique_ptr *pp) 
{
    std::cout << "*pp,  m_name: " << (*pp)->m_name << std::endl;
}

int main(void)
{
    std::unique_ptr pu1(new City("Shenzhen"));
    std::unique_ptr pu2(new City("Shenzhen"));
    std::unique_ptr pu3(new City("Shenzhen"));
    BeautifulCity1(pu1);
    // BeautifulCity2(pu2); // 错误,删除了拷贝构造,不能直接赋值。
    BeautifulCity2(std::move(pu2)); 
    BeautifulCity3(&pu3);

    return 0;
}

输出:

jiazhao@MININT-04QBDDV:~/samples$ g++ smart_pointer_unique_ptr.cpp && ./a.out 
City(Shenzhen)
City(Shenzhen)
City(Shenzhen)
&pp, m_name: Shenzhen
pp,  m_name: Shenzhen
~City(Shenzhen)
*pp,  m_name: Shenzhen
~City(Shenzhen)
~City(Shenzhen)
jiazhao@MININT-04QBDDV:~/samples$ 
  • unique_ptr 赋值给另一个时,如果源 unique_ptr 是一个临时右值,编译器允许这样做; 如果源 unique_ptr 将存在一段时间,编译器禁止这样做。一般用于函数返回值。
std::unique_ptr GetCity()
{
    std::unique_ptr pu(new City("Shanghai"));
    return pu;
}

int main (void)
{
    std::unique_ptr pu1(new City("Shenzhen"));
    std::unique_ptr pu2;
    // pu2 = pu1; // 错误
    pu2 = std::unique_ptr(new City("Beijing")); // 用匿名对象赋值

    std::cout << "before" << std::endl;
    pu2 = GetCity();
    std::cout << "after" << std::endl;

    return 0;
}

输出:

jiazhao@MININT-04QBDDV:~/samples$ g++ smart_pointer_unique_ptr.cpp && ./a.out 
City(Shenzhen)
City(Beijing)
before
City(Shanghai)
~City(Beijing)
after
~City(Shanghai)
~City(Shenzhen)
jiazhao@MININT-04QBDDV:~/samples$ 
  • nullptrunique_ptr 赋值将释放对象,空的 unique_ptr == nullptr
int main(void)
{
    std::unique_ptr pu(new City("Shenzhen"));

    std::cout << (pu == nullptr ? "Empty." : "Not empty.") << std::endl;
    pu = nullptr;
    std::cout << (pu == nullptr ? "Empty." : "Not empty.") << std::endl;

    return 0;
}

输出:

jiazhao@MININT-04QBDDV:~/samples$ g++ smart_pointer_unique_ptr.cpp && ./a.out 
City(Shenzhen)
Not empty.
~City(Shenzhen)
Empty.
jiazhao@MININT-04QBDDV:~/samples$ 
  • release() 释放对原始指针的控制权,将 unique_ptr 置为空,返回裸指针。可用于把 unique_ptr 传递给子函数,子函数将负责释放对象。
  • std::move() 可以转移对原始指针的控制权。可用于把 unique_ptr 传递给子函数,子函数形参也是 unique_ptr
void VisitCity1(const City* city)
{
    std::cout << "VisitCity1: " << city->m_name << std::endl;
}

void VisitCity2(City* city)
{
    std::cout << "VisitCity2: " << city->m_name << std::endl;
    delete city;
}

void VisitCity3(const std::unique_ptr& pu)
{
    std::cout << "VisitCity3: " << pu->m_name << std::endl;
}

void VisitCity4(std::unique_ptr pu)
{
    std::cout << "VisitCity4: " << pu->m_name << std::endl;
}

int main(void)
{
    std::unique_ptr pu(new City("Shenzhen"));
    std::cout << "Start..." << std::endl;
    // VisitCity1(pu.get()); // 把原始指针传给子函数,不释放控制权,子函数不能释放原始指针。
    VisitCity2(pu.release()); // 把原始指针传给子函数,释放控制权,由子函数释放原始指针。
    // VisitCity3(pu);   // 引用传递。
    // VisitCity4(std::move(pu)); // 值传递,要借助std::move。
    std::cout << "End." <

输出:

jiazhao@MININT-04QBDDV:~/samples$ g++ smart_pointer_unique_ptr.cpp && ./a.out 
City(Shenzhen)
Start...
VisitCity2: Shenzhen
~City(Shenzhen)
End.
jiazhao@MININT-04QBDDV:~/samples$ 
  • reset() 释放对象
// void reset(T* _ptr = (T*)nullptr);
int main(void)
{
    std::unique_ptr pu1(new City("Shenzhen"));
    std::cout << "pu1: "  << pu1.get() << std::endl;
    pu1.reset(); // 重置指向的原始指针,默认置为nullptr。
    std::cout << "pu1: "  << pu1.get() << std::endl;

    std::unique_ptr pu2(new City("Beijing"));
    std::cout << "pu2: "  << pu2.get() << std::endl;
    pu2.reset(nullptr);
    std::cout << "pu2: "  << pu2.get() << std::endl;

    std::unique_ptr pu3(new City("Shanghai"));
    std::cout << "pu3: " << pu3.get() << std::endl;
    pu3.reset(new City("Tianjing")); // 释放旧的原始指针,指向新的普通指针。
    std::cout << "pu3: "  << pu3.get() << std::endl;

    return 0;
}

输出:

jiazhao@MININT-04QBDDV:~/samples$ g++ smart_pointer_unique_ptr.cpp && ./a.out 
City(Shenzhen)
pu1: 0x55e436bf6eb0
~City(Shenzhen)
pu1: 0
City(Beijing)
pu2: 0x55e436bf6eb0
~City(Beijing)
pu2: 0
City(Shanghai)
pu3: 0x55e436bf6eb0
City(Tianjing)
~City(Shanghai)
pu3: 0x55e436bf72f0
~City(Tianjing)
jiazhao@MININT-04QBDDV:~/samples$ 
  • swap() 交换两个 unique_ptr 的控制权。
int main(void)
{
    std::unique_ptr pu1(new City("ShenZhen"));
    std::unique_ptr pu2(new City("Beijing"));

    std::cout << "pu1: " << pu1.get() << std::endl;
    std::cout << "pu2: " << pu2.get() << std::endl;

    pu1.swap(pu2);

    std::cout << "pu1: " << pu1.get() << std::endl;
    std::cout << "pu2: " << pu2.get() << std::endl;

    return 0;
}

输出:

jiazhao@MININT-04QBDDV:~/samples$ g++ smart_pointer_unique_ptr.cpp && ./a.out 
City(ShenZhen)
City(Beijing)
pu1: 0x556cc09b6eb0
pu2: 0x556cc09b72f0
pu1: 0x556cc09b72f0
pu2: 0x556cc09b6eb0
~City(ShenZhen)
~City(Beijing)
jiazhao@MININT-04QBDDV:~/samples$ 
  • unique_ptr 也可像普通指针那样,当指向一个类继承体系的基类对象时,也具有多态性质,如同使用裸指针管理基类对象和派生类对象那样。
class Car
{
public:
    virtual void Driving()
    {
        std::cout << "Driving." << std::endl;
    }

    virtual void Parking()
    {
        std::cout << "Parking." << std::endl;
    }

    virtual void Washing()
    {
        std::cout << "Washing." << std::endl;
    }
};

class Biyadi : public Car
{
public:
    void Driving() override
    {
        std::cout << "Driving Biyadi." << std::endl;
    }

    void Parking() override
    {
        std::cout << "Parking Biyadi." << std::endl;
    }

    void Washing() override
    {
        std::cout << "Washing Biyadi." << std::endl;
    }
};

class Xiaopeng : public Car
{
public:
    void Driving() override
    {
        std::cout << "Driving Xiaopeng." << std::endl;
    }

    void Parking() override
    {
        std::cout << "parking Xiaopeng." << std::endl;
    }
    void Washing() override
    {
        std::cout << "Washing Xiaopeng." << std::endl;
    }
};

class Weilai : public Car
{
public:
    void Driving() override
    {
        std::cout << "Driving Weilai." << std::endl;
    }
    void Parking() override
    {
        std::cout << "parking Weilai." << std::endl;
    }
    void Washing() override
    {
        std::cout << "Washing Weilai." << std::endl;
    }
};

int main(void)
{
    int id = 0;
    std::cout << "Input: (1 - Biyadi, 2 - Xiaopeng, 3 - Weilai): ";
    std::cin >> id;

    std::unique_ptr pu;

    if (id == 1)
    {
        pu = std::unique_ptr(new Biyadi);
    }
    if (id == 2) 
    {
        pu = std::unique_ptr(new Xiaopeng);
    }
    if (id == 3)
    {
        pu = std::unique_ptr(new Weilai);
    }

    if (pu != nullptr)
    {
        pu->Driving();
        pu->Parking();
        pu->Washing();
    }

    return 0;
}

输出:

jiazhao@MININT-04QBDDV:~/samples$ g++ smart_pointer_unique_ptr.cpp && ./a.out 
Input: (1 - Biyadi, 2 - Xiaopeng, 3 - Weilai): 2
Driving Xiaopeng.
parking Xiaopeng.
Washing Xiaopeng.
jiazhao@MININT-04QBDDV:~/samples$ 
  • unique_ptr 不是绝对安全
    如程序用 exit() 退出,全局 unique_ptr 可自动释放,但局部非 static unique_ptr 无法释放。
    操作空的 unique_ptr 也有可能出现野指针的情况。
std::unique_ptr g_pu(new City("Shenzhen"));
int main(void)
{
    std::unique_ptr pu(new City("Beijing")); // 不会自动释放
    static std::unique_ptr s_pu(new City("Shanghai")); // 会释放,全局静态区
    exit(0); // 局部智能指针pu不会释放
    // return 0; // 全局和局部的智能指针都会释放
}

输出:

jiazhao@MININT-04QBDDV:~/samples$ g++ smart_pointer_unique_ptr.cpp && ./a.out 
City(Shenzhen)
City(Beijing)
City(Shanghai)
~City(Shanghai)
~City(Shenzhen)
jiazhao@MININT-04QBDDV:~/samples$ 
  • unique_ptr 提供支持数组的具体化版本,数组版本的 unique_ptr ,重载操作符 [],操作符 [] 返回的是引用,可以作为左值使用。
int main(void)
{
    // City *parr1 = new City[2];
    // // City *parr1 = new City[2]{std::string(""), std::string("")};
    // parr1[0].m_name = "Shenzhen";
    // parr1[1].m_name = "Beijing";
    // std::cout << parr1[0].m_name << std::endl;
    // std::cout << parr1[1].m_name << std::endl;
    // delete [] parr1;

    // std::unique_ptr parr2(new City[2]);
    std::unique_ptr parr2(new City[2]{std::string("Shenzhen"), std::string("Beijing")});

    parr2[0].m_name = "Shenzhen";
    parr2[1].m_name = "Beijing";

    std::cout << parr2[0].m_name << std::endl;
    std::cout << parr2[1].m_name << std::endl;

    return 0;
}

输出:

jiazhao@MININT-04QBDDV:~/samples$ g++ smart_pointer_unique_ptr.cpp && ./a.out 
City(Shenzhen)
City(Beijing)
Shenzhen
Beijing
~City(Beijing)
~City(Shenzhen)
jiazhao@MININT-04QBDDV:~/samples$ 

示例代码 - shared_ptr

#include 
#include 

class City
{
public:
    std::string m_name;
    City()
    {
        std::cout << "City()" << std::endl;
    }

    City(std::string name) : m_name(name)
    {
        std::cout << "City(" << m_name << ")" << std::endl;
    }

    ~City()
    {
        std::cout << "~City(" << m_name << ")" << std::endl;
    }
};

int main (void)
{
    // // 方法一:
    // City *p = new City("Shenzhen");
    // std::shared_ptr p0(p);

    // // 方法二:
    // std::shared_ptr p0(new City("Shenzhen"));

    // 方法三:
    std::shared_ptr p0 = std::make_shared("Shenzhen");
    
    // 方法四:
    std::shared_ptr p1(p0);
    std::shared_ptr p2 = p0;

    std::cout << "p0.use_count() = " << p0.use_count() << std::endl;
    std::cout << "p0->m_name = " << p0->m_name << std::endl;
    std::cout << "p0.get() = " << p0.get() << std::endl;
    
    std::cout << "p1.use_count() = " << p1.use_count() << std::endl;
    std::cout << "p1->m_name = " << p1->m_name << std::endl;
    std::cout << "p1.get() = " << p1.get() << std::endl;

    std::cout << "p2.use_count() = " << p2.use_count() << std::endl;
    std::cout << "p2->m_name = " << p2->m_name << std::endl;
    std::cout << "p2.get() = " << p2.get() << std::endl;

    std::cout << "p2.unique() = " << p2.unique() << std::endl;

    return 0;
}

输出:

jiazhao@MININT-04QBDDV:~/samples$ g++ smart_pointer_shared_ptr.cpp && ./a.out 
City(Shenzhen)
p0.use_count() = 3
p0->m_name = Shenzhen
p0.get() = 0x555d01152ec0
p1.use_count() = 3
p1->m_name = Shenzhen
p1.get() = 0x555d01152ec0
p2.use_count() = 3
p2->m_name = Shenzhen
p2.get() = 0x555d01152ec0
p2.unique() = 0
~City(Shenzhen)
jiazhao@MININT-04QBDDV:~/samples$ 
  • 支持普通的拷贝和赋值, 左值shared_ptr 的计数器减1,右值shared_ptr计数器加 1
int main(void)
{
    std::shared_ptr pa0(new City("Shenzhen")); // 初始化shared_ptr
    std::shared_ptr pa1 = pa0;                 // 拷贝构造,计数器加1
    std::shared_ptr pa2 = pa0;                 // 拷贝构造,计数器加1
    std::cout << "pa0.use_count() = " << pa0.use_count() << std::endl; // 值为3

    std::shared_ptr pb0(new City("Beijing"));
    std::shared_ptr pb1 = pb0;
    std::cout << "pb0.use_count() = " << pb0.use_count() << std::endl; // 值为2

    pb1 = pa1;

    std::cout << "pa0.use_count() = " << pa0.use_count() << std::endl; // 值为4
    std::cout << "pb0.use_count() = " << pb0.use_count() << std::endl; // 值为1

    pb0 = pa1; // 计数器变为0,会释放pb0,即调用析构函数 ~City(Beijing)。

    std::cout << "pa0.use_count() = " << pa0.use_count() << std::endl; // 值为5
    std::cout << "pb0.use_count() = " << pb0.use_count() << std::endl; // 值为5

    return 0;
}

输出:

jiazhao@MININT-04QBDDV:~/samples$ g++ smart_pointer_shared_ptr.cpp && ./a.out 
City(Shenzhen)
pa0.use_count() = 3
City(Beijing)
pb0.use_count() = 2
pa0.use_count() = 4
pb0.use_count() = 1
~City(Beijing)
pa0.use_count() = 5
pb0.use_count() = 5
~City(Shenzhen)
jiazhao@MININT-04QBDDV:~/samples$ 

示例代码 - weak_ptr

  • shared_ptr 存在的问题。
    shared_ptr 内部维护一个共享引用计数器,多个 shared_ptr 可指向同一资源。如果出现了循环引用情况,引用计数永远无法归 0,资源不会被释放。
#include 
#include 

class Country; //Country 类要提前声明。

class City
{
public:
    std::string m_name;
    std::shared_ptr m_p;
    City()
    {
        std::cout << "City()" << std::endl;
    }

    City(std::string name) : m_name(name)
    {
        std::cout << "City(" << m_name << ")" << std::endl;
    }

    ~City()
    {
        std::cout << "~City(" << m_name << ")" << std::endl;
    }
};

class Country
{
public:
    std::string m_name;
    std::shared_ptr m_p;
    Country()
    {
        std::cout << "Country()" << std::endl;
    }

    Country(std::string name) : m_name(name)
    {
        std::cout << "Country(" << m_name << ")" << std::endl;
    }

    ~Country()
    {
        std::cout << "~Country(" << m_name << ")" << std::endl;
    }
};

int main(void)
{
    std::shared_ptr pa = std::make_shared("Shenzhen");
    std::shared_ptr pb = std::make_shared("China");

    pa->m_p = pb;
    pb->m_p = pa;

    std::cout << "pa.use_count() = " << pa.use_count() << std::endl; // 引用计数器为2,main函数退出时不析构
    std::cout << "pb.use_count() = " << pb.use_count() << std::endl; // 引用计数器为2,main函数退出时不析构

    // 解决方法用std::weak_ptr
    return 0;
}

输出:

jiazhao@MININT-04QBDDV:~/samples$ g++ smart_pointer_weak_ptr.cpp && ./a.out 
City(Shenzhen)
Country(China)
pa.use_count() = 2
pb.use_count() = 2
jiazhao@MININT-04QBDDV:~/samples$ 
  • 引入 weak_ptr
#include 
#include 

class Country;

class City
{
public:
    std::string m_name;
    std::weak_ptr m_p; // 改成 weak_ptr
    City()
    {
        std::cout << "City()" << std::endl;
    }

    City(std::string name) : m_name(name)
    {
        std::cout << "City(" << m_name << ")" << std::endl;
    }

    ~City()
    {
        std::cout << "~City(" << m_name << ")" << std::endl;
    }
};

class Country
{
public:
    std::string m_name;
    std::weak_ptr m_p; // 改成 weak_ptr
    Country()
    {
        std::cout << "Country()" << std::endl;
    }

    Country(std::string name) : m_name(name)
    {
        std::cout << "Country(" << m_name << ")" << std::endl;
    }

    ~Country()
    {
        std::cout << "~Country(" << m_name << ")" << std::endl;
    }
};

int main(void)
{
    std::shared_ptr pa = std::make_shared("Shenzhen");
    std::shared_ptr pb = std::make_shared("China");

    pa->m_p = pb; // weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。
    pb->m_p = pa;

    std::cout << "pa.use_count() = " << pa.use_count() << std::endl; // 引用计数器为1, 可以正常析构
    std::cout << "pb.use_count() = " << pb.use_count() << std::endl; // 引用计数器为1, 可以正常析构

    return 0;
}

输出:

jiazhao@MININT-04QBDDV:~/samples$ g++ smart_pointer_weak_ptr.cpp && ./a.out 
City(Shenzhen)
Country(China)
pa.use_count() = 1
pb.use_count() = 1
~Country(China)
~City(Shenzhen)
jiazhao@MININT-04QBDDV:~/samples$ 
  • 判断它指向的资源是否已过期(过期则销毁),并用 lock() 函数提升为 shared_ptr
int main(void)
{
    std::shared_ptr pa = std::make_shared("Shenzhen");

    {
        std::shared_ptr pb = std::make_shared("China");

        pa->m_p = pb;
        pb->m_p = pa;
        
        // 非线程安全
        if (pa->m_p.expired() == true)
        {
            std::cout << "pa->m_p.expired() = true" << std::endl;
        }
        else
        {
            std::cout << "pa->m_p.lock()->m_name = " << pa->m_p.lock()->m_name << std::endl;
        }
    }

    if (pa->m_p.expired() == true)
    {
        std::cout << "pa->m_p.expired() = true" << std::endl;
    }
    else
    {
        std::cout << "pa->m_p.lock()->m_name = " << pa->m_p.lock()->m_name << std::endl;
    }

    return 0;
}

输出:

jiazhao@MININT-04QBDDV:~/samples$ g++ smart_pointer_weak_ptr.cpp && ./a.out 
City(Shenzhen)
Country(China)
pa->m_p.lock()->m_name = China
~Country(China)
pa->m_p.expired() = true
~City(Shenzhen)
jiazhao@MININT-04QBDDV:~/samples$ 
  • lock() 提升行为线程安全。
int main(void)
{
    std::shared_ptr pa = std::make_shared("Shenzhen");

    {
        std::shared_ptr pb = std::make_shared("China");
        pa->m_p = pb;
        pb->m_p = pa;

        std::shared_ptr pp = pa->m_p.lock(); // 线程安全/原子操作。
        if (pp == nullptr)
        {
            std::cout << "pa->m_p.lock() = nullptr" << std::endl;
        }
        else
        {
            std::cout << "pa->m_p.lock()->m_name = " << pp->m_name << std::endl;
        }
    }

    std::shared_ptr pp = pa->m_p.lock(); // pb 生命周期结束,此处返回空。
    if (pp == nullptr)
    {
        std::cout << "pa->m_p.lock() = nullptr" << std::endl;
    }
    else
    {
        std::cout << "pa->m_p.lock()->m_name = " << pp->m_name << std::endl;
    }

    return 0;
}

输出:

jiazhao@MININT-04QBDDV:~/samples$ g++ smart_pointer_weak_ptr.cpp && ./a.out 
City(Shenzhen)
Country(China)
pa->m_p.lock()->m_name = China
~Country(China)
pa->m_p.lock() = nullptr
~City(Shenzhen)
jiazhao@MININT-04QBDDV:~/samples$ 

示例代码 - 删除器

#include 
#include 

class City
{
public:
    std::string m_name;
    City()
    {
        std::cout << "City()" << std::endl;
    }

    City(std::string name) : m_name(name)
    {
        std::cout << "City(" << m_name << ")" << std::endl;
    }

    ~City()
    {
        std::cout << "~City(" << m_name << ")" << std::endl;
    }
};

void DeleteFunction(City *city) // 删除器,普通函数
{
    std::cout << "DeleteFunction(" << city->m_name << ")" << std::endl;
    delete city;
}

struct DeleteClass // 删除器,仿函数
{
    void operator()(City *city)
    {
        std::cout << "DeleteClass(" << city->m_name << ")" << std::endl;
        delete city;
    }
};

auto DeleteLambda = [](City *city) { // 删除器,Lambda表达式
    std::cout << "DeleteLambda(" << city->m_name << ")" << std::endl;
    delete city;
};

int main(void)
{
    // std::shared_ptr pa1(new City("Shenzhen")); // 使用缺省的删除器,即用delete关键字删除。
    // std::shared_ptr pa2(new City("Beijing"), DeleteFunction); // 删除器,普通函数。
    // std::shared_ptr pa3(new City("Shanghai"), DeleteClass()); // 删除器,仿函数。
    // std::shared_ptr pa4(new City("Guangzhou"), DeleteLambda); // 删除器,Lambda表达式。

    // std::unique_ptr pu0(new City("Shenzhen"));
    std::unique_ptr pu1(new City("Beijing"), DeleteFunction);
    // std::unique_ptr pu2(new City("Shanghai"), DeleteFunction);
    // std::unique_ptr pu3(new City("Guangzhou"), DeleteClass());
    // std::unique_ptr pu4(new City("Tianjin"), DeleteLambda);
    
    return 0;
}

输出:

jiazhao@MININT-04QBDDV:~/samples$ g++ smart_pointer_deleter.cpp && ./a.out 
City(Beijing)
DeleteFunction(Beijing)
~City(Beijing)
jiazhao@MININT-04QBDDV:~/samples$ 

你可能感兴趣的:(C/C++,c++,开发语言,c语言)