c++ 自学笔记

编程入门 行业动态 更新时间:2024-10-15 10:13:14

c++ 自学<a href=https://www.elefans.com/category/jswz/34/1770047.html style=笔记"/>

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 开始,定义在 <memory> 中,独享它指向的对象,也就是说,同时只有一个 unique_ptr 指向同一个对象,当这个 unique_ptr 被销毁时,指向的对象也随之被销毁。

  • 初始化

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

    • 智能指针重载了 *-> 操作符,可以像使用指针一样使用 unique_ptr
      std::unique_ptr<City> pu3 = std::make_unique<City>("Shenzhen");
      std::cout << "m_name = " << (*pu3).m_name << std::endl;
      std::cout << "m_name = " << pu3->m_name << std::endl;
      
    • 不支持普通的拷贝和赋值。
      std::unique_ptr<City> pu1(new City("Shenzhen"));
      City *p = new City("Shenzhen");// 错误,不能把普通指针直接赋值给智能指针。
      std::unique_ptr<City> pu2 = p;
      // 错误,不能把普通指针直接赋值给智能指针。
      std::unique_ptr<City> pu3 = new City("Shenzhen"); 
      // 错误,不能用其它的unique_ptr拷贝构造。
      std::unique_ptr<City> pu2 = pu1;std::unique_ptr<City> pu3;
      pu3 = pu1;// 错误,不能用“=”对unique_ptr进行赋值。
      
    • 不要用同一个裸指针初始化多个 unique_ptr
      City *p = new City("Shenzhen");
      std::unique_ptr<City> pu1(p);
      // std::unique_ptr<City> pu2(p); // 错误,不能把普通指针传给多个智能指针。
      // std::unique_ptr<City> pu3(p); // 错误,不能把普通指针传给多个智能指针
      
    • get() 方法返回裸指针。
      City *p = new City("Shenzhen");
      std::unique_ptr<City> pu(p);std::cout << "p       : " << p << std::endl;
      std::cout << "pu.get(): " << pu.get() <<std::endl;
      std::cout << "&pu     : " << &pu << std::endl;
      
    • 不要用 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<City> pu1(new City("ShenZhen"));
      std::unique_ptr<City> 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<City> p0(p);
      
    • C++11 标准,效率更高。

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

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

      std::shared_ptr<City> p0(new City("Shenzhen"));
      std::shared_ptr<City> p1(p0);
      std::shared_ptr<City> 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<City> pa1(new City("Shenzhen")); // 使用缺省的删除器,即用delete关键字删除。// std::shared_ptr<City> pa2(new City("Beijing"), DeleteFunction); // 删除器,普通函数。// std::shared_ptr<City> pa3(new City("Shanghai"), DeleteClass()); // 删除器,仿函数。// std::shared_ptr<City> pa4(new City("Guangzhou"), DeleteLambda); // 删除器,Lambda表达式。// std::unique_ptr<City> pu0(new City("Shenzhen"));std::unique_ptr<City, decltype(DeleteFunction) *> pu1(new City("Beijing"), DeleteFunction);// std::unique_ptr<City, void (*)(City *)> pu2(new City("Shanghai"), DeleteFunction);// std::unique_ptr<City, DeleteClass> pu3(new City("Guangzhou"), DeleteClass());// std::unique_ptr<City, decltype(DeleteLambda)> 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 <iostream>
#include <memory>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<City> pu1(p);std::cout << "m_name = " << (*p).m_name << std::endl;std::cout << "m_name = " << p->m_name << std::endl;// 方法二:std::unique_ptr<City> pu2(new City("Shenzhen"));// 方法三 (std::make_unique C++14 标准):std::unique_ptr<City> pu3 = std::make_unique<City>("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<City> pu1(new City("Shenzhen"));City *p = new City("Beijing");// std::unique_ptr<City> pu2 = p;                    // 错误,不能把普通指针直接赋值给智能指针。// std::unique_ptr<City> pu3 = new City("Shenzhen"); // 错误,不能把普通指针直接赋值给智能指针。// std::unique_ptr<City> pu2 = pu1;                  // 错误,不能用其它的unique_ptr拷贝构造。std::unique_ptr<City> 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<City> pu1(p);std::unique_ptr<City> pu2(p); // 错误,不能把普通指针传给多个智能指针。std::unique_ptr<City> 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<City> 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<City> &pp) 
{std::cout << "&pp, m_name: " << pp->m_name << std::endl;
}// 2)传值: 删除了拷贝构造,不能直接传值,可用:BeautifulCity(std::move(pu));
void BeautifulCity2(std::unique_ptr<City> pp) 
{std::cout << "pp,  m_name: " << pp->m_name << std::endl;
}// 3) 传指针:BeautifulCity3(&pu)
void BeautifulCity3(std::unique_ptr<City> *pp) 
{std::cout << "*pp,  m_name: " << (*pp)->m_name << std::endl;
}int main(void)
{std::unique_ptr<City> pu1(new City("Shenzhen"));std::unique_ptr<City> pu2(new City("Shenzhen"));std::unique_ptr<City> 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<City> GetCity()
{std::unique_ptr<City> pu(new City("Shanghai"));return pu;
}int main (void)
{std::unique_ptr<City> pu1(new City("Shenzhen"));std::unique_ptr<City> pu2;// pu2 = pu1; // 错误pu2 = std::unique_ptr<City>(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<City> 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<City>& pu)
{std::cout << "VisitCity3: " << pu->m_name << std::endl;
}void VisitCity4(std::unique_ptr<City> pu)
{std::cout << "VisitCity4: " << pu->m_name << std::endl;
}int main(void)
{std::unique_ptr<City> 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." <<std::endl;return 0;
}

输出:

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<City> pu1(new City("Shenzhen"));std::cout << "pu1: "  << pu1.get() << std::endl;pu1.reset(); // 重置指向的原始指针,默认置为nullptr。std::cout << "pu1: "  << pu1.get() << std::endl;std::unique_ptr<City> pu2(new City("Beijing"));std::cout << "pu2: "  << pu2.get() << std::endl;pu2.reset(nullptr);std::cout << "pu2: "  << pu2.get() << std::endl;std::unique_ptr<City> 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<City> pu1(new City("ShenZhen"));std::unique_ptr<City> 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<Car> pu;if (id == 1){pu = std::unique_ptr<Car>(new Biyadi);}if (id == 2) {pu = std::unique_ptr<Car>(new Xiaopeng);}if (id == 3){pu = std::unique_ptr<Car>(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<City> g_pu(new City("Shenzhen"));
int main(void)
{std::unique_ptr<City> pu(new City("Beijing")); // 不会自动释放static std::unique_ptr<City> 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<City[]> parr2(new City[2]);std::unique_ptr<City[]> 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 <iostream>
#include <memory>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<City> p0(p);// // 方法二:// std::shared_ptr<City> p0(new City("Shenzhen"));// 方法三:std::shared_ptr<City> p0 = std::make_shared<City>("Shenzhen");// 方法四:std::shared_ptr<City> p1(p0);std::shared_ptr<City> 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<City> pa0(new City("Shenzhen")); // 初始化shared_ptrstd::shared_ptr<City> pa1 = pa0;                 // 拷贝构造,计数器加1std::shared_ptr<City> pa2 = pa0;                 // 拷贝构造,计数器加1std::cout << "pa0.use_count() = " << pa0.use_count() << std::endl; // 值为3std::shared_ptr<City> pb0(new City("Beijing"));std::shared_ptr<City> pb1 = pb0;std::cout << "pb0.use_count() = " << pb0.use_count() << std::endl; // 值为2pb1 = pa1;std::cout << "pa0.use_count() = " << pa0.use_count() << std::endl; // 值为4std::cout << "pb0.use_count() = " << pb0.use_count() << std::endl; // 值为1pb0 = pa1; // 计数器变为0,会释放pb0,即调用析构函数 ~City(Beijing)。std::cout << "pa0.use_count() = " << pa0.use_count() << std::endl; // 值为5std::cout << "pb0.use_count() = " << pb0.use_count() << std::endl; // 值为5return 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 <iostream>
#include <memory>class Country; //Country 类要提前声明。class City
{
public:std::string m_name;std::shared_ptr<Country> 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<City> 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<City> pa = std::make_shared<City>("Shenzhen");std::shared_ptr<Country> pb = std::make_shared<Country>("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_ptrreturn 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 <iostream>
#include <memory>class Country;class City
{
public:std::string m_name;std::weak_ptr<Country> m_p; // 改成 weak_ptrCity(){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<City> m_p; // 改成 weak_ptrCountry(){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<City> pa = std::make_shared<City>("Shenzhen");std::shared_ptr<Country> pb = std::make_shared<Country>("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<City> pa = std::make_shared<City>("Shenzhen");{std::shared_ptr<Country> pb = std::make_shared<Country>("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<City> pa = std::make_shared<City>("Shenzhen");{std::shared_ptr<Country> pb = std::make_shared<Country>("China");pa->m_p = pb;pb->m_p = pa;std::shared_ptr<Country> 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<Country> 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 <iostream>
#include <memory>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<City> pa1(new City("Shenzhen")); // 使用缺省的删除器,即用delete关键字删除。// std::shared_ptr<City> pa2(new City("Beijing"), DeleteFunction); // 删除器,普通函数。// std::shared_ptr<City> pa3(new City("Shanghai"), DeleteClass()); // 删除器,仿函数。// std::shared_ptr<City> pa4(new City("Guangzhou"), DeleteLambda); // 删除器,Lambda表达式。// std::unique_ptr<City> pu0(new City("Shenzhen"));std::unique_ptr<City, decltype(DeleteFunction) *> pu1(new City("Beijing"), DeleteFunction);// std::unique_ptr<City, void (*)(City *)> pu2(new City("Shanghai"), DeleteFunction);// std::unique_ptr<City, DeleteClass> pu3(new City("Guangzhou"), DeleteClass());// std::unique_ptr<City, decltype(DeleteLambda)> 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++ 自学笔记

本文发布于:2024-03-05 07:06:08,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1711635.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:笔记

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!