C++新特性-智能指针

ops/2024/9/24 10:01:14/

std::weak_ptr

std::weak_ptr是一种弱引用,它不能单独使用,设计之初是为了配合std::shared_ptr,解决后者设计上存在的问题。

  • 使用注意:

    • 不能直接指向原始指针:std::weak_ptr<int> wp (new int);
    • 只能指向std::shared_ptr对象或者std::weak_ptr对象
    • 不增加引用计数
    • 可以用expired()来检测指向的std::shared_ptr管理的对象是否被析构了。
    • 不能直接使用std::shared_ptr管理的对象,如果要使用需要调用lock()。如果底层的对象还没被析构,那么就会返回一个std::shared_ptr指针,指向该对象,否则返回nullptr
  • 构造函数

        constexpr weak_ptr() noexcept;weak_ptr( const weak_ptr& r ) noexcept;weak_ptr( weak_ptr&& r ) noexcept;template< typename Y >weak_ptr( const weak_ptr<Y>& r ) noexcept;template< typename Y >weak_ptr( weak_ptr<Y>&& r ) noexcept;template< typename Y >weak_ptr( const std::shared_ptr<Y>& r ) noexcept;
    

    从构造函数可见,std::weak_ptr只能接受std::weak_ptrstd::shared_ptr类型,而不能std::weak_ptr<T> wp (new T);

    移动语义下的构造函数,构造完成 r 将会变成 nullptr ,不可用。

    std::weak_ptr 的正确使用场景是那些资源如果可能就使用,如果不可使用则不用的场景,它不参与资源的生命周期管理。例如,网络分层结构中,Session 对象(会话对象)利用 Connection 对象(连接对象)提供的服务工作,但是 Session 对象不管理 Connection 对象的生命周期,Session 管理 Connection 的生命周期是不合理的,因为网络底层出错会导致 Connection 对象被销毁,此时 Session 对象如果强行持有 Connection 对象与事实矛盾。

  • std::weak_ptr 主要有两个用途:

它只能配合std::shared_ptr使用,不能单独使用。

  • 防止 std::shared_ptr循环引用
    如果两个std::shared_ptr相互引用,那么就会形成一个环,引用计数无法变成0,也会导致内存泄漏。
  class Foo : public std::enable_shared_from_this<Foo> { public:Foo(){ std::cout<<"ctor\n"; }~Foo(){ std::cout<<"dtor\n"; }void self() { fptr_ = shared_from_this();}private:std::shared_ptr<Foo> fptr_; // 改 fptr_ 为 std::weak_ptr 类型即可};int main() { {std::shared_ptr<Foo> fptr = std::make_shared<Foo>();fptr->self();}   return 0;}
  • std::enable_shared_from_this<T>::shared_from_this
    这是个侵入式设计。为的解决传入this导致对象被析构两次的问题。
    什么情况下需要使用 shared_from_this() ??? 用于返回当前对象 *thisstd::shared_ptr类型指针时:

       class Foo : public enable_shared_from_this<Foo>{public:Foo(){ std::cout<<"Foo ctor.\n";}~Foo(){std::cout<<"Foo dtor.\n";}std::shared_ptr<Foo> getSelf(){ return shared_from_this();}};int main() {Foo* foo = new Foo; std::shared_ptr<Foo> sp1(foo);std::shared_ptr<Foo> sp2 = sp1->getSelf();  // 为了对 foo对象进行共享std::cout<<std::boolalpha;std::cout<<(sp2.get()== foo)<<std::endl;std::cout<<sp1.use_count()<<std::endl;} 
    

    函数原型

       template<typename _Tp>class enable_shared_from_this {protected:...public:shared_ptr<_Tp>shared_from_this() { return shared_ptr<_Tp>(this->_M_weak_this); }shared_ptr<const _Tp>shared_from_this() const { return shared_ptr<const _Tp>(this->_M_weak_this); }private:...mutable weak_ptr<_Tp>  _M_weak_this;}
    

    enable_shared_from_this的子类需要返回自身的std::shared_ptr指针,那么就需要继承这个类。

  • 成员变量为什么是weak_ptr类型
    因为如果是std::shared_ptr类型,那么就永远无法析构对象自身。

    这个_M_weak_this不是这个类中初始化,而是在shared_ptr中初始化,初始化的值就是this。因此如果智能指针类型是std::shared_ptr,那么这个类对象一旦创建,引用计数就是1,那么永远也无法析构。

  • 为什么不直接传回this
    std::shared_ptr的引用计数增加是需要用operator=实现的。

        class Foo {/** ... */};int main() {Foo* foo = new Foo;std::shared_ptr<Foo> sp1(foo);std::shared_ptr<Foo> sp2(foo);std::cout<<sp1.use_count()<<std::endl; // 输出是1}  
    

    也就是说,尽管sp1sp2都指向了foo,但是却不共享计数,当析构的时候就会被析构两次,产生未定义行为。
    std::weak_ptr可以接受std::shared_ptr参数来构造自己,std::shared_ptr也具有接受std::weak_ptr参数来构造自己。

智能指针问题

  • share_ptrunique_ptr区别

    主要在于前者采用引用技术实现对象共享,redis里的对象管理也是采用这个。而后者只能独占,不能赋值/复制,只能移动,因为其拷贝构造函数和赋值函数被禁用了。

    std::unique_ptr内部:

        // Disable copy from lvalue.unique_ptr(const unique_ptr&) = delete;unique_ptr& operator=(const unique_ptr&) = delete;
    
参考
  • 循环引用
  • shared_from_tbis
shared_ptr 指向一个动态数组需要注意什么?

析构器需要设置为 delete[]。而uniqued_ptr的的默认析构器模板类std::default_delete,能自动设别new intnew int[]因为不用担心。

  template< class Y > explicit shared_ptr( Y* ptr );template< class Y, class Deleter >  shared_ptr( Y* ptr, Deleter d )

上面一个,默认的析构器是delete ptr。当 Y* ptr = new Y[x],析构器也需要重置。

  #include <memory>#include <vector>#include <algorithm>int main() {//   {//        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[]>}
std::shared_ptr 线程安全 ?

std::shared_ptr的引用计数本身是安全且无锁的,但对象的读写则不是。也就是说std::shared_ptr对象的创建析构是线程安全的,但是多线程读写std::shared_ptr对象不是线程安全的。std::shared_ptr 内存是由于两个组成部分: 指向管理对象的指针 和 引用计数器。在读/写时,是直接对两个变量操作,不可能是原子类型的。因为 std::shared_ptr 有两个数据成员,读写操作不能原子化.使得多线程读写同一个 std::shared_ptr 对象需要加锁.

std::weak_ptr的实现原理

std::weak_ptr 是为了解决 std::shared_ptr 循环引用而生,构造 std::weak_ptr 对象只能通过 std::shared_ptr 来构造,但是std::weak_ptr 对象的生命周期对相应的 std::shared_ptr的引用计数不产生影响,即不增加或者减少引用计数。

std::weak_ptr的引用计数部分也是有锁操作,因此 std::weak_ptr 对象生命周期的构造与销毁都是线程安全的。

	// 基类template<typename _Tp, _Lock_policy _Lp>class __weak_ptr{template<typename _Yp, typename _Res = void>using _Compatible = typename enable_if<__sp_compatible_with<_Yp*, _Tp*>::value, _Res>::type;// Constraint for assignment from shared_ptr and weak_ptr:template<typename _Yp>using _Assignable = _Compatible<_Yp, __weak_ptr&>;public:using element_type = typename remove_extent<_Tp>::type;constexpr __weak_ptr() noexcept: _M_ptr(nullptr), _M_refcount(){ }__weak_ptr(const __weak_ptr&) noexcept = default;~__weak_ptr() = default;// The "obvious" converting constructor implementation:////  template<typename _Tp1>//  __weak_ptr(const __weak_ptr<_Tp1, _Lp>& __r)//  : _M_ptr(__r._M_ptr), _M_refcount(__r._M_refcount) // never throws//  { }//// has a serious problem.////  __r._M_ptr may already have been invalidated. The _M_ptr(__r._M_ptr)//  conversion may require access to *__r._M_ptr (virtual inheritance).//// It is not possible to avoid spurious access violations since// in multithreaded programs __r._M_ptr may be invalidated at any point.template<typename _Yp, typename = _Compatible<_Yp>>__weak_ptr(const __weak_ptr<_Yp, _Lp>& __r) noexcept: _M_refcount(__r._M_refcount){ _M_ptr = __r.lock().get(); }template<typename _Yp, typename = _Compatible<_Yp>>__weak_ptr(const __shared_ptr<_Yp, _Lp>& __r) noexcept: _M_ptr(__r._M_ptr), _M_refcount(__r._M_refcount){ }__weak_ptr(__weak_ptr&& __r) noexcept: _M_ptr(__r._M_ptr), _M_refcount(std::move(__r._M_refcount)){ __r._M_ptr = nullptr; }template<typename _Yp, typename = _Compatible<_Yp>>__weak_ptr(__weak_ptr<_Yp, _Lp>&& __r) noexcept: _M_ptr(__r.lock().get()), _M_refcount(std::move(__r._M_refcount)){ __r._M_ptr = nullptr; }__weak_ptr&operator=(const __weak_ptr& __r) noexcept = default;template<typename _Yp>_Assignable<_Yp>operator=(const __weak_ptr<_Yp, _Lp>& __r) noexcept{_M_ptr = __r.lock().get();_M_refcount = __r._M_refcount;return *this;}template<typename _Yp>_Assignable<_Yp>operator=(const __shared_ptr<_Yp, _Lp>& __r) noexcept{_M_ptr      = __r._M_ptr;_M_refcount = __r._M_refcount;return *this;}__weak_ptr&operator=(__weak_ptr&& __r) noexcept{_M_ptr      = __r._M_ptr;_M_refcount = std::move(__r._M_refcount);__r._M_ptr  = nullptr;return *this;}template<typename _Yp>_Assignable<_Yp>operator=(__weak_ptr<_Yp, _Lp>&& __r) noexcept{_M_ptr      = __r.lock().get();_M_refcount = std::move(__r._M_refcount);__r._M_ptr  = nullptr;return *this;}__shared_ptr<_Tp, _Lp> lock() const noexcept{ return __shared_ptr<element_type, _Lp>(*this, std::nothrow); }long use_count() const noexcept{ return _M_refcount._M_get_use_count(); }bool expired() const noexcept{ return _M_refcount._M_get_use_count() == 0; }template<typename _Tp1>bool owner_before(const __shared_ptr<_Tp1, _Lp>& __rhs) const noexcept{ return _M_refcount._M_less(__rhs._M_refcount); }template<typename _Tp1>bool owner_before(const __weak_ptr<_Tp1, _Lp>& __rhs) const noexcept{ return _M_refcount._M_less(__rhs._M_refcount); }void reset() noexcept{ __weak_ptr().swap(*this); }void swap(__weak_ptr& __s) noexcept{std::swap(_M_ptr, __s._M_ptr);_M_refcount._M_swap(__s._M_refcount);}private:// Used by __enable_shared_from_this.void _M_assign(_Tp* __ptr, const __shared_count<_Lp>& __refcount) noexcept{if (use_count() == 0){_M_ptr = __ptr;_M_refcount = __refcount;}}template<typename _Tp1, _Lock_policy _Lp1> friend class __shared_ptr;template<typename _Tp1, _Lock_policy _Lp1> friend class __weak_ptr;friend class __enable_shared_from_this<_Tp, _Lp>;friend class enable_shared_from_this<_Tp>;element_type*	 	 _M_ptr;       	 // Contained pointer.__weak_count<_Lp>  _M_refcount;    // Reference counter.};/***  @brief  A smart pointer with weak semantics.**  With forwarding constructors and assignment operators.*/template<typename _Tp>class weak_ptr : public __weak_ptr<_Tp>{template<typename _Arg> using _Constructible = typename enable_if<is_constructible<__weak_ptr<_Tp>, _Arg>::value>::type;template<typename _Arg>using _Assignable = typename enable_if<is_assignable<__weak_ptr<_Tp>&, _Arg>::value, weak_ptr&>::type;public:constexpr weak_ptr() noexcept = default;template<typename _Yp,typename = _Constructible<const shared_ptr<_Yp>&>>weak_ptr(const shared_ptr<_Yp>& __r) noexcept: __weak_ptr<_Tp>(__r) { }weak_ptr(const weak_ptr&) noexcept = default;template<typename _Yp, typename = _Constructible<const weak_ptr<_Yp>&>>weak_ptr(const weak_ptr<_Yp>& __r) noexcept: __weak_ptr<_Tp>(__r) { }weak_ptr(weak_ptr&&) noexcept = default;template<typename _Yp, typename = _Constructible<weak_ptr<_Yp>>>weak_ptr(weak_ptr<_Yp>&& __r) noexcept: __weak_ptr<_Tp>(std::move(__r)) { }weak_ptr&operator=(const weak_ptr& __r) noexcept = default;template<typename _Yp>_Assignable<const weak_ptr<_Yp>&>operator=(const weak_ptr<_Yp>& __r) noexcept{this->__weak_ptr<_Tp>::operator=(__r);return *this;}template<typename _Yp>_Assignable<const shared_ptr<_Yp>&>operator=(const shared_ptr<_Yp>& __r) noexcept{this->__weak_ptr<_Tp>::operator=(__r);return *this;}weak_ptr&operator=(weak_ptr&& __r) noexcept = default;template<typename _Yp>_Assignable<weak_ptr<_Yp>>operator=(weak_ptr<_Yp>&& __r) noexcept{this->__weak_ptr<_Tp>::operator=(std::move(__r));return *this;}shared_ptr<_Tp> lock() const noexcept{ return shared_ptr<_Tp>(*this, std::nothrow); }};

http://www.ppmy.cn/ops/93291.html

相关文章

系统架构师案例分析题的解题技巧

1. 仔细阅读题目 确保理解题目所描述的系统背景、需求和问题。标注关键信息&#xff0c;如系统的规模、业务特点、性能要求等。 2. 明确问题类型 判断是关于架构设计原则、质量属性优化、技术选型还是其他方面的问题。例如&#xff0c;如果是关于性能优化的问题&#xff0c;要重…

MySQL 存储引擎之MyISAM

MyISAM 是 MySQL 中的一个存储引擎&#xff0c;它在 MySQL 的早期版本中非常流行&#xff0c;并且在某些特定的应用场景下仍然有其独特的优势。 MyISAM 存储引擎特点&#xff1a; 不支持事务&#xff1a;这意味着一旦发生崩溃&#xff0c;恢复过程可能比较复杂。表锁&#xff…

部分性能探索

函数节流 函数节流是指在一定时间内只执行一次函数&#xff0c;比如鼠标滚动事件&#xff0c;如果没有限制&#xff0c;那么每次鼠标滚动都会触发大量事件&#xff0c;导致浏览器卡顿甚至崩溃&#xff0c;而通过函数节流可以使得事件执行稳定流畅。 function throttle(fn, wai…

同构字符串

给定两个字符串 s 和 t &#xff0c;判断它们是否是同构的。 如果 s 中的字符可以按某种映射关系替换得到 t &#xff0c;那么这两个字符串是同构的。 每个出现的字符都应当映射到另一个字符&#xff0c;同时不改变字符的顺序。不同字符不能映射到同一个字符上&#xff0c;相…

Django中的Q对象

文章目录 Django中的Q对象三种使用方式Q对象中常用的属性或方法 Django中的Q对象 from django.db.models import Q在Django框架中&#xff0c;Q 对象是 django.db.models.Q 的一个实例&#xff0c;它用于创建复杂的查询表达式。 Q 对象允许你构建包含多个条件的查询&#xff0c…

【网络】代理服务器

目录 正向代理 反向代理 个人主页&#xff1a;东洛的克莱斯韦克-CSDN博客 正向代理 正向代理&#xff08;Forward Proxy&#xff09;是一种常见的网络代理方式&#xff0c;它位于客户端和目标 服务器之间&#xff0c;代表客户端向目标服务器发送请求。正向代理服务器接收客户…

【深度学习】卷积神经网络(CNN)简介

文章目录 1. 前言2. 什么是卷积神经网络&#xff08;CNN&#xff09;&#xff1f;2.1 卷积神经网络&#xff08;CNN&#xff09;的背景2.2 CNN 如何运作&#xff1f; 3. 人工神经元在 CNN 中的作用4. CNN 中的特征提取5. 什么是池化层&#xff1f;6. 卷积神经网络&#xff08;C…

2024年8月12日(LVS)

1、描述以及工作原理 &#xff08;1)什么是LVS linux virtural server的简称,也就是linxu虚拟机服务器,这是一个由章文嵩博士发起的开 源项目,官网是http://www.linuxvirtualserver.org,现在lvs已经是linux内核标准的一 部分,使用lvs可以达到的技术目标是:通过Linux达到负载均…