为了支持对 RAII 原则的简单采用,C++ 标准库提供了三种智能指针类型:
====================================
unique_ptr
unique_ptr 类型智能指针在设计上最显著的特点是内部托管的指针一旦被创建就不能被任何形式的复制给另一个unique_ptr,只可以被移动给另一个unique_ptr。unique_ptr 没有拷贝构造函数,因此不能用于赋值。该指针最常用的情况是单例模式和编译防火墙的封装。
如果非要抬杠,使用 get() 函数获取到裸指针给另外一个裸指针,那么你使用智能指针的意义又何在呢?
任何智能指针都不应该去 get 裸指针使用,更不能 delete!
// 演示创建 unique_ptrunique_ptr<Brain>u_brain=make_unique<Brain>(); u_brain->HelloWorld(); // 移动 unique_ptrunique_ptr<Brain>um_barin=std::move(u_brain); um_barin->HelloWorld(); // 移动方法2std::swap(u_brain, um_brain); // 错误// um_barin = u_brain;// u_brain->HelloWorld(); // C26800// 可以使用以下方法判断是否为空指针if (um_brain==nullptr) { std::cout<<"um_brain is nullptr"<<std::endl; } // 可以释放资源将指针恢复空指针um_brain.reset();
====================================
shared_ptr
和 unique 不同的是,它允许自身对象(shared_ptr)被复制,复制出来的 shared_ptr 所托管的指针都指向同一块内存空间。而它的每一份拷贝(shared_ptr自身)都会有一个引用计数,资源的释放由生命周期中最后一个 shared_ptr 负责。因此 shared_ptr 是最常用的智能指针,也是最容易出问题的智能指针。
使用它时应当注意:
1,不要将已存在裸指针交由 shared_ptr,任何形式的智能指针都不应该去托管已有的裸指针。
2,作为函数参数传递时,请传递引用。因为作为值传递时,将产生大量无意义的引用计数。
3,共享所有权性质的对象往往比限定作用域的对象生存时间更久、资源开销更大,尤其是多线程下。
// 创建shared_ptr<Brain>s_brain=make_shared<Brain>(); s_brain->HelloWorld(); // 复制shared_ptr<Brain>c_brain=s_brain; shared_ptr<Brain>d_brain=s_brain; // 检查唯一性std::cout<<s_brain.unique() <<std::endl; // 检查引用计数数量std::cout<<s_brain.use_count() <<std::endl;
与 shared_ptr 搭配的 weak_ptr
weak_ptr 设计上与 shared_ptr 搭配使用,因为 shared_ptr 存在一个问题,就是循环引用计数递增而导致的内存泄漏。
比如说:
【伪代码】
classnode{ shared_ptr<node>start; shared_ptr<node>end; } shared_ptr<node>nn=make; shared_ptr<node>mm=make; nn->end=mm; mm->start=nn;
这种情况,两个node对象互相引用着对方,最终导致资源无法被释放。所以有时候需要访问 shared_ptr 自身,而不是它所托管的资源。
所以 weak_ptr 的作用就来了:
【伪代码】
classnode{ weak_ptr<node>start; weak_ptr<node>end; } shared_ptr<node>nn=make; shared_ptr<node>mm=make; nn->end=mm; mm->start=nn;
为什么呢?
因为 share_ptr 是强引用,强引用是只要被引用的对象还存活那么这个引用也一定会存在。
而 weak_ptr 是弱引用,弱引用是虽然对象还活着,但是这个引用则可有可无。
所以,weak_ptr 的作用就是作为一个 "观察者" 访问 shared_ptr 本身,而不是 shared_ptr 所托管的资源。
同时也意味着,weak_ptr 只能访问它所观察的 shared_ptr 本身,而不能访问 share_ptr 托管的资源,所以,它不会增加 shared_ptr 的引用计数。
shared_ptr<Brain>r_brain=make_shared<Brain>(); // 创建 weak_ptrweak_ptr<Brain>w_brain(r_brain); // 检查 shared 引用计数数量std::cout<<w_brain.use_count() <<std::endl; // 返回一个原始 shared 的拷贝(增加引用计数)shared_ptr<Brain>e_brain(w_brain.lock()); std::cout<<w_brain.use_count() <<std::endl;
====================================
make_unique 与 make_shared
这两个标准库函数是用于创建智能指针的函数。
【以下懒得打字了直接抄的Docs,重点我划出来】
autosp=std::shared_ptr<Example>(newExample(argument)); automsp=std::make_shared<Example>(argument);
使用make_shared作为创建对象的简单、更高效的方法,以及一个shared_ptr来同时管理对对象的共享访问。 在语义上,这两个语句是等效的。但是,第一条语句进行了两个分配,如果在shared_ptr对象的分配成功后,Example的分配失败,则未命名的Example对象将被泄漏。 使用make_shared的语句更简单,因为只涉及到一个函数调用。 这样会更有效,因为库可能会对对象和智能指针进行一个分配。 此函数的速度更快,导致内存碎片更少,但在一次分配时不存在异常,而不是在另一种分配上。 通过使引用对象和更新智能指针中的引用计数的代码具有的更好的地址来提高性能。
make_unique 如果不需要对对象的共享访问权限,请考虑使用。 allocate_shared 如果需要为对象指定自定义分配器,请使用。 make_shared如果对象需要自定义删除器,则不能使用,因为无法将删除器作为参数传递。
--Microsoft Docs
====================================
芯片烤电池 C++ Example 2022-Spring Season Pass :
【Example】C++ 回调函数及 std::function 与 std::bind
【Example】C++ 标准库智能指针 unique_ptr 与 shared_ptr
【Example】C++ Template (模板)概念讲解及编译避坑
【Example】C++ 标准库 std::thread 与 std::mutex
【Example】C++ 标准库多线程同步及数据共享 (std::future 与 std::promise)
【Example】C++ 标准库 std::condition_variable
【Example】C++ 用于编译时封装的 Pimpl 演示 (编译防火墙 Private-IMPL)
【Example】C++ 单例模式 演示代码 (被动模式、兼容VS2022编译)
====================================