-
2019-08-07 00:53:51
weak_ptr描述
声明
头文件:
<memory>
模版类:template <class T> class weak_ptr
声明方式:std::weak_ptr<type_id> statement
作用
根据boost库的官方描述,weak_ptr是由shared_ptr管理的一种弱引用对象的模版类。
weak_ptr
的对象能够使用shared_ptr
指针的构造函数转换为一个shared_ptr对象。但是这里shared_ptr
的构造函数参数需要包含weak_ptr
的lock成员,该成员是weak_ptr用来获取shared_ptr的指针。这样当shared_ptr在多线程过程中被销毁时
shared_ptr::reset
,weak_ptr的lock成员仍然能够保留shared_ptr的成员,直到当前shared_ptr正常终止,否则会出现非常危险的内存泄漏。关于lock()成员的作用如下描述:
如下代码shared_ptr<int> p(new int(5)); weak_ptr<int> q(p); // some time later if(int * r = q.get()) { // use *r }
多线程环境中shared_ptr是可以被多个线程共享,在r被使用之前p对象执行了
p.reset()
,正如我们上一篇文章中对shared_ptr的描述(C++智能指针:shared_ptr 实现详解),reset成员会重置当前shared_ptr指针的指向。此时,当前线程如果继续使用r指针,势必会产生访问空地址的异常问题根据以上问题,使用
weak_ptr::lock()
成员来解决该问题shared_ptr<int> p(new int(5)); weak_ptr<int> q(p); // some time later //使用weak_ptr的lock成员来获取shared_ptr的指针 if(shared_ptr<int> r = q.lock()) { // use *r }
关于lock()成员简单说明一下,lock成员获取到的shared_ptr p指针创建一个临时对象(我们weak_ptr弱引用的体现),这个临时对象同样指向p,即使p执了reset这样的delete引用的操作,弱引用对象仍然持有改智能指针的地址,直到r指针的生命周期结束才会释放。不得不佩服C++语言设计者的脑洞,为了保持C++对内存操作的自由,即使耗费再大的精力也要实现这一目标,工匠精神才让今天的C++越来越被底层程序员喜欢。
原理实现
源码文件
/boost/smart_ptr/weak_ptr.hpp
template<class T> class weak_ptr { private: // Borland 5.5.1 specific workarounds typedef weak_ptr<T> this_type;
constructor
构造函数//默认构造函数 weak_ptr() BOOST_NOEXCEPT : px(0), pn() // never throws in 1.30+ { } //拷贝构造函数 weak_ptr( weak_ptr const & r ) BOOST_NOEXCEPT : px( r.px ), pn( r.pn ) { }
destructor
析构函数,这里weak_ptr使用的是默认析构函数,一般使用expired
返回空对象或者user_count()为0的情况则辅助shared_ptr释放引用operator=
1. weak_ptr & operator=( weak_ptr && r ) BOOST_NOEXCEPT { this_type( static_cast< weak_ptr && >( r ) ).swap( *this ); return *this; } //2.这里会更加安全,使用lock成员获取Y类型的指针 template<class Y> weak_ptr & operator=( weak_ptr<Y> const & r ) BOOST_NOEXCEPT { boost::detail::sp_assert_convertible< Y, T >(); px = r.lock().get(); pn = r.pn; return *this; } 3. template<class Y> weak_ptr & operator=( shared_ptr<Y> const & r ) BOOST_NOEXCEPT { boost::detail::sp_assert_convertible< Y, T >(); px = r.px; pn = r.pn; return *this; }
weak_ptr::swap
成员,交换两个weak_ptr所指内容以及地址void swap(this_type & other) BOOST_NOEXCEPT { //先交换地址,再交换内容 std::swap(px, other.px); pn.swap(other.pn); }
weak_ptr::reset
成员,·重新指定对象地址和内容,就像是重新使用默认构造函数进行了初始化void reset() BOOST_NOEXCEPT // never throws in 1.30+ { //使用默认构造函数构造的对象和当前对象进行swap操作 this_type().swap(*this); }
weak_ptr::use_count
成员,获取shared_ptr对象被引用的次数。如果为空,则返回0long use_count() const BOOST_NOEXCEPT { return pn.use_count(); }
weak_ptr::expired
成员,当根据use_count==0来返回bool,其返回为true的时候,使用lock获取weak_ptr的指针只能获取到空指针bool expired() const BOOST_NOEXCEPT { return pn.use_count() == 0; }
weak_ptr::lock
成员,会向weak_ptr对象返回一个shared_ptr。正如我们之前在weak_ptr作用中所描述的,防止多线程访问时的shared_ptr内存泄漏。此时weak_ptr对象获取到的指针为临时指针,会指向shared_ptr对象之前所指向的地址。shared_ptr<T> lock() const BOOST_NOEXCEPT { return shared_ptr<T>( *this, boost::detail::sp_nothrow_tag() ); }
函数成员使用
- 构造函数
输出如下:#include <iostream> #include <memory> struct C {int* data;}; int main () { std::shared_ptr<int> sp (new int); std::weak_ptr<int> wp1; std::weak_ptr<int> wp2 (wp1); std::weak_ptr<int> wp3 (sp); std::cout << "use_count:\n"; //weak_ptr对象如果为经shared_ptr初始化, //它是没有引用计数的,所以这里wp1和wp2引用计数都为0 //只有wp3经过了shared_ptr初始化,它的引用计数才为1 std::cout << "wp1: " << wp1.use_count() << '\n'; std::cout << "wp2: " << wp2.use_count() << '\n'; std::cout << "wp3: " << wp3.use_count() << '\n'; return 0; }
use_count: wp1: 0 wp2: 0 wp3: 1
weak_ptr::operator=
赋值运算符
输出如下// weak_ptr::operator= example #include <iostream> #include <memory> int main () { std::shared_ptr<int> sp1,sp2; std::weak_ptr<int> wp; // sharing group: // -------------- sp1 = std::make_shared<int> (10); // sp1 wp = sp1; // sp1, wp sp2 = wp.lock(); // sp1, wp, sp2 sp1.reset(); // wp, sp2 //通过lock保留的临时指针,重新获取到了shared_ptr共享的地址 sp1 = wp.lock(); // sp1, wp, sp2 std::cout << "*sp1: " << *sp1 << '\n'; std::cout << "*sp2: " << *sp2 << '\n'; return 0; }
*sp1: 10 *sp2: 10
std::weak_ptr::swap
交换指针地址以及对应的内容
输出如下:#include <iostream> #include <memory> int main () { std::shared_ptr<int> sp1 (new int(10)); std::shared_ptr<int> sp2 (new int(20)); std::weak_ptr<int> wp1(sp1); std::weak_ptr<int> wp2(sp2); std::cout << "wp1 -> " << *wp1.lock() << " " << wp1.lock() << '\n'; std::cout << "wp2 -> " << *wp2.lock() << " " << wp2.lock() << '\n'; //这里的swap仅仅是交换wp2的各自的指向地址 //并不会直接导致对应智能指针原始指针的地址交换 //根据输出,所以很明显,交换完成之后weak_ptr对象指向发生了变化,但是并未导致share_ptr指针的指向变化 wp1.swap(wp2); std::cout << "sp1 -> " << *sp1 << " " << sp1 << '\n'; std::cout << "sp2 -> " << *sp2 << " " << sp2 << '\n'; std::cout << "wp1 -> " << *wp1.lock() << " " << wp1.lock() << '\n'; std::cout << "wp2 -> " << *wp2.lock() << " " << wp2.lock() << '\n'; return 0; }
wp1 -> 10 0x11daf90 wp2 -> 20 0x11dafd0 sp1 -> 10 0x11daf90 sp2 -> 20 0x11dafd0 wp1 -> 20 0x11dafd0 wp2 -> 10 0x11daf90
std::weak_ptr::reset
成员,执行之后weak_ptr对象就像是重新执行了默认构造函数,又变成了一个空的对象
输出如下// weak_ptr::reset example #include <iostream> #include <memory> int main () { std::shared_ptr<int> sp (new int(10)); std::weak_ptr<int> wp(sp); std::cout << "1. wp " << (wp.expired()?"is":"is not") << " expired\n"; std::cout << "4. wp " << wp.use_count() << " *wp " << *wp.lock() << '\n'; wp.reset(); std::cout << "2. wp " << (wp.expired()?"is":"is not") << " expired\n"; std::cout << "3. sp " << sp.use_count() << " *sp " << *sp << '\n'; return 0; }
1. wp is not expired 2. wp 2 *wp 10 3. wp is expired 4. sp 1 *sp 10
总结
shared_ptr和weak_ptr主要区别如下
- shared_ptr对象能够初始化实际指向一个地址内容而weak_ptr对象没办法直接初始化一个具体地址,它的对象需要由shared_ptr去初始化
- weak_ptr不会影响shared_ptr的引用计数,因为它是一个弱引用,只是一个临时引用指向shared_ptr。即使用shared_ptr对象初始化weak_ptr不会导致shared_ptr引用计数增加。依此特性可以解决shared_ptr的循环引用问题。
- weak_ptr没有解引用*和获取指针->运算符,它只能通过lock成员函数去获取对应的shared_ptr智能指针对象,从而获取对应的地址和内容。
参考文档:
http://www.cplusplus.com/reference/memory/weak_ptr/
https://www.boost.org/doc/libs/1_66_0/libs/smart_ptr/doc/html/smart_ptr.html#weak_ptr更多相关内容 -
【C++ 深入浅出】智能指针shared_ptr、unique_ptr、weak_ptr详解
2021-01-07 03:12:57C++ 11中最常用的智能指针类型为shared_ptr,它采用引用计数的方法,记录当前内存资源被多少个智能指针引用。该引用计数的内存在堆上分配。当新增一个时引用计数加1,当过期时引用计数减一。只有引用计数为0时,智能... -
C++11智能指针之weak_ptr详解
2020-08-19 02:37:59主要介绍了 C++11智能指针之weak_ptr详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下 -
c++三种智能指针shared_ptr、weak_ptr、unique_ptr的原理和使用
2022-05-18 12:58:14c++智能指针总结 一、智能指针出现的原因 1. 基于我们的的编程习惯,在堆区动态管理的资源忘记释放或者回收了,导致内存泄漏。 2. 有多个指针指向同一...二、智能指针的本质及其原理 智能指针的本质一个对象,是一个c++智能指针总结
一、智能指针出现的原因
1. 基于我们的的编程习惯,在堆区动态管理的资源忘记释放或者回收了,导致内存泄漏。
2. 有多个指针指向同一片内存的问题,造成内存资源的重复释放或回收。
3. 程序在在抛出异常前申请了资源,以至于异常抛出时导致程序中断,无法执行析构函数delete内存从而导致的内存泄漏。基于上面三个主要的原因,聪明的程序员就提出了智能指针方便管理我们自己的内存,一定程度上解决了c++为了人所诟病的内存管理问题。
注意:三种指针包含在头文件< memory >.二、智能指针的本质及其原理
- 智能指针的本质一个对象,是一个行为表现都像指针的对象。它的封装利用了RAII机制或者思想,其RAII机制或者思想是“资源获取就是初始化”,是C++语言的一种管理资源、避免泄漏的惯用法。
- 智能指针可分为两部分,一个是原指针,一个是引用计数(关联的计数器)。创建一个智能指针的时候引用计数为1,当引用计数为0的时候,智能指针本身会自动释放。当该智能指针被其他指针所用,引用计数就会相应的叠加,可以这么理解:引用计数的大小就是当前管理该内存的指针的数量。
三、三种智能指针的介绍
1.shared_ptr
1.初始化和赋值(注意不要用一个原始指针初始化多个shared_ptr 会导致重复释放内存)
#include <memory> #include<iostream> int main() { int* a = new int(10); std::shared_ptr<int>s1(a); std::shared_ptr<int>s2(new int(10)); std::shared_ptr<int>s3 = s1; std::shared_ptr<int>s4=make_shared<int>(30); auto s5 = std::make_shared<int>(20); return 0; }
2.重载原始指针的“*”和“->”
#include <memory> #include<iostream> class Person { public: Person(int a) :a(a){} public: int a; }; int main() { std::shared_ptr<Person>s(new Person(2)); //与原始指针的使用方式一样 std::cout << (*s).a << std::endl; std::cout << s->a << std::endl; return 0; }
3.其他成员函数的作用和使用
#include <memory> #include<iostream> #include<algorithm> class Person { public: Person(int a) :a(a){} public: int a; }; int main() { std::shared_ptr<Person>s(new Person(2)); std::cout << (*s).a << std::endl; std::cout << s->a << std::endl; std::cout << s.use_count() << std::endl;//得到引用计数的数量即当前管理该内存的指针的个数 Person*p = s.get();//得到原始的指针 std::shared_ptr<Person>s2 = s; std::cout << s.use_count() << std::endl; s.~shared_ptr();//析构函数它的作用是use_count()--然后其检查是否为0 若为0就释放,若不为0选择无视 std::cout << s2.use_count() << std::endl;//此处打印为1说明上面析构函数没有释放内存资源 std::shared_ptr<Person>s3(new Person(4)); s3.swap(s2);//交换管理的内存,交换指针 swap(s2, s3);//跟上面效果一样 if (s2.unique())std::cout << "资源是被唯一管理的" << std::endl; //用来判断资源是否是被唯一管理的 s2.reset();//强制释放资源,将s2的use_count()置为0 指针指向nullptr s3.reset(new Person(10));//重置资源 std::cout<< s3->a<< std::endl; return 0; }
2.weak_ptr
关于弱指针,我们先要追究一下它的来由,看下面代码:
#include <memory> #include<iostream> class Son; class Father; using fatherptr = std::shared_ptr<Father>; using sonptr = std::shared_ptr<Son>; class Father { public: sonptr s; Father(); ~Father(); }; class Son { public: fatherptr f; Son(); ~Son(); }; Father::Father() { std::cout << "hello father"<<std::endl; } Father::~Father() { std::cout << "bye father"<<std::endl;} Son::Son() { std::cout << "hellow son"<<std::endl; } Son::~Son() { std::cout << "bye son" << std::endl; } using fatherptr = std::shared_ptr<Father>; using sonptr = std::shared_ptr<Son>; int main() { fatherptr f(new Father()); sonptr s(new Son()); f->s = s; s->f = f; //循环调用 std::cout << f.use_count() << std::endl;//输出2 std::cout << s.use_count() << std::endl;//输出2 return 0; }
有如下结果:
发现它并没有调用智能指针指向的对象的析构函数!!为啥呢??
原因是:在智能指针调用析构函数的时候先将use_count- -当use_count等于0的时候,释放所指对象的内存资源,但是这里它发现use_count等于1,相当于给它传递一个信息,还有指针在用这一片资源,不能释放!从而导致了这里内存的泄露,这也是share_ptr在这种循环调用中的缺陷!!那么怎么解决,聪明的程序员就开发出了weak_ptr,可以这么说,weak_ptr就是为了协助share_ptr而生。它本身不具备指针的功能没有重载的“*”和“->”.
它的成员函数都是为了监测shared_ptr所管理的资源而设计的。#include <memory> #include<iostream> class Person { public: Person(int a) :a(a) {} public: int a; }; int main() { std::shared_ptr<Person>s(new Person(2)); std::weak_ptr<Person>w = s; std::weak_ptr<Person>w2(s); std::weak_ptr<Person>w3(w2); //weak_ptr从一个shared_ptr或者weak_ptr进行对象构造的初始化 std::cout << s.use_count() << std::endl;//输出为1,说明weak_ptr构造它的时候引用计数不会增加 //weak_ptr没有参与资源管理,只是对资源的监控 std::cout << w2.use_count() << std::endl; auto s2 = w.lock();//返回一个指向该资源的shared_ptr if(w.expired())//判读weak_ptr监控的shared_ptr是不是空资源 相当于use_cout==0,但是这种方式更快 {std::cout<<"shared_ptr指向的是空区域"<<std::endl; } else {std::cout<<"shared_ptr管理的资源不是空" ;} return 0; }
那等于循环调用 我们如何通过弱指针来解决呢?
#include <memory> #include<iostream> #include<algorithm> class Son; class Father; using fatherptr = std::shared_ptr<Father>; using sonweakptr = std::weak_ptr<Son>; using sonptr = std::shared_ptr<Son>; class Father { public: sonweakptr s; Father(); ~Father(); }; class Son { public: fatherptr f; Son(); ~Son(); }; Father::Father() { std::cout << "hello father"<<std::endl; } Father::~Father() { std::cout << "bye father"<<std::endl;} Son::Son() { std::cout << "hellow son"<<std::endl; } Son::~Son() { std::cout << "bye son" << std::endl; } int main() { fatherptr f(new Father()); sonptr s(new Son()); f->s = s; s->f = f; //循环调用 std::cout << f.use_count() << std::endl; std::cout << s.use_count() << std::endl; return 0; }
我们把其中一个成员指针改成弱指针,就有如下结果:
这样就解决了shared_ptr循环调用的问题。
3.unique_ptr
unique顾名思义,唯一的指向一个对象,该对象不能被共享(指针和资源一对一),unique_ptr向比于原始指针,使得在出现异常的情况下动态资源得以释放,unique_ptr的释放规则是:unique_ptr从指针开始,到离开作用域时,释放其指向的对象资源。
unique_ptr用法:#include<iostream> #include<memory> int main() { std::unique_ptr<int> u(new int(10));//绑定申请的堆区资源 std::unique_ptr<int>u2(std::move(u));//转移所有权到u2(移动语义) //不允许同一份资源,被多个unique_Ptr管理 //std::unique_ptr<int>u2(u);不能拷贝 //std::unique_ptr<int>u3=u;不能赋值 auto p=u.release();//释放所有权 返回原指针,相当于unique_ptr不参与原指针的管理了 u.reset(new int(20));//重新制定所有权 std::shared_ptr s(std::move(u2));//在有需要的时候,我们也可以将它转移到shared_ptr中管理。 }
其余操作和shared_ptr类似。
shared_ptr常见错误补充
如果我们想在类的内部调用自身的智能指针,我们肯定会想到用this指针初始化一个智能指针,如下面的代码:
#include <memory> #include<iostream> #include<algorithm> class Son; class Father; using fatherptr = std::shared_ptr<Father>; using faweakptr = std::weak_ptr<Father>; using sonweakptr = std::weak_ptr<Son>; using sonptr = std::shared_ptr<Son>; void handptr(const fatherptr& f, const sonptr& s)//增加测试函数作为 作为类中成员函数调用自身智能指针的定义实现 { std::cout << "测试函数" << std::endl; } class Father { public: sonweakptr s; Father(); ~Father(); void test(); }; class Son { public: fatherptr f; Son(); ~Son(); }; Father::Father() { std::cout << "hello father"<<std::endl; } Father::~Father() { std::cout << "bye father"<<std::endl;} void Father::test() { handptr(fatherptr(this), s.lock()); }//在类内部成员函数调用自身智能指针 Son::Son() { std::cout << "hellow son"<<std::endl; } Son::~Son() { std::cout << "bye son" << std::endl; } int main() { fatherptr f(new Father()); sonptr s(new Son()); f->s = s; s->f = f; f->test();//调用测试 std::cout << f.use_count() << std::endl; std::cout << s.use_count() << std::endl; return 0; }
我们会有如下结果:
发生了意料之中的错误,听一片内存被释放了两次。
这时候我们就要引入新知识std::enable_shared_from_this,这是一个模板类,用法代码如下:#include <memory> #include<iostream> #include<algorithm> class Son; class Father; using fatherptr = std::shared_ptr<Father>; using faweakptr = std::weak_ptr<Father>; using sonweakptr = std::weak_ptr<Son>; using sonptr = std::shared_ptr<Son>; void handptr(const fatherptr& f, const sonptr& s)//增加测试函数作为 作为类中成员函数调用自身智能指针的定义实现 { std::cout << "测试函数" << std::endl; } class Father:public std::enable_shared_from_this<Father>//继承该模板类,传入本身类型 { public: sonweakptr s; Father(); ~Father(); void test(); }; class Son:public std::enable_shared_from_this<Son> { public: fatherptr f; Son(); ~Son(); }; Father::Father() { std::cout << "hello father"<<std::endl; } Father::~Father() { std::cout << "bye father"<<std::endl;} void Father::test() { handptr(shared_from_this(), s.lock()); }//传入父类的继承函数成员函数,该成员函数返回一个this的智能指针 Son::Son() { std::cout << "hellow son"<<std::endl; } Son::~Son() { std::cout << "bye son" << std::endl; } int main() { fatherptr f(new Father()); sonptr s(new Son()); f->s = s; s->f = f; f->test();//调用测试 std::cout << f.use_count() << std::endl; std::cout << s.use_count() << std::endl; return 0; }
总结
智能指针能给我们资源管理带来极大的便捷,但是凡是都有两面性,智能指针所带来的便捷,其实是由性能消耗换来的,在追求极致性能的时候,不要盲目使用智能指针。
写了一个下午,给菜鸡一个👍鼓励一下吧~~
-
C++ 智能指针(shared_ptr/weak_ptr)源码
2016-06-10 19:46:01C++ 智能指针(shared_ptr/weak_ptr)源码 源码位置:gcc-6.1.0\gcc-6.1.0\libstdc++-v3\include\tr1 这里只单列shared_ptr.h文件用于分析 -
智能指针原理剖析(二):shared_ptr、weak_ptr
2020-11-24 20:56:31智能指针原理剖析(一):auto_ptr、unique_ptr shared_ptr、weak_ptr原理剖析 关于shared_ptr源码剖析的博客有很多,推荐一篇讲解十分详细的博客:从源码理解智能指针(二)—— shared_ptr、weak_ptr。本文在此...智能指针原理剖析(一):auto_ptr、unique_ptr
shared_ptr、weak_ptr原理剖析
关于shared_ptr源码剖析的博客有很多,推荐一篇讲解十分详细的博客:从源码理解智能指针(二)—— shared_ptr、weak_ptr。本文在此基础上,对shared_ptr、weak_ptr实现的原理进行总结并画出类关系图,读者可结合两篇博客一起阅读,从而帮助理解。
总体而言,实现shared_ptr、weak_ptr智能指针是通过两个基类_Ref_count_base、_Ptr_base以及它们的派生类来实现的,其结构图如下:
1、抽象类_Ref_count_base
class _Ref_count_base { // common code for reference counting private: virtual void _Destroy() = 0; virtual void _Delete_this() = 0; private: _Atomic_counter_t _Uses; //强引用计数 _Atomic_counter_t _Weaks; //弱引用计数 protected: _Ref_count_base() { // construct _Init_atomic_counter(_Uses, 1); //强引用计数初始化为1 _Init_atomic_counter(_Weaks, 1); //弱引用计数初始化为1 } public: virtual ~_Ref_count_base() _NOEXCEPT //虚析构函数 { // ensure that derived classes can be destroyed properly } unsigned int _Get_uses() const //获取强引用计数 { // return use count return (_Get_atomic_count(_Uses)); } void _Incref() { // increment use count _MT_INCR(_Mtx, _Uses);// _Uses+1 } void _Incwref() { // increment weak reference count _MT_INCR(_Mtx, _Weaks);//_Weaks+1 } void _Decref() { // decrement use count if (_MT_DECR(_Mtx, _Uses) == 0) { // destroy managed resource, decrement weak reference count _Destroy(); //释放资源 _Decwref(); } } void _Decwref() { // decrement weak reference count if (_MT_DECR(_Mtx, _Weaks) == 0) //判断弱引用计数-1后是否为0 _Delete_this(); //删除计数器自身 } long _Use_count() const //返回强引用计数 { // return use count return (_Get_uses()); } bool _Expired() const //强引用计数是否为0 { // return true if _Uses == 0 return (_Get_uses() == 0); } virtual void *_Get_deleter(const _XSTD2 type_info&) const { // return address of deleter object return (0); } };
_Ref_count_base是派生类_Ref_count、_Ref_count_del、_Ref_count_del_alloc的基类,是计数器的接口。基类中纯虚函数virtual void _Destroy() = 0、virtual void _Delete_this() = 0是实现多态的接口,具体定义在三个子类中实现,分别表示释放资源、删除计数器自身。
_Uses是强引用计数,计算引用资源的shared_ptr个数,_Weaks是弱引用计数,计算引用资源的weak_ptr的个数。 _Uses和_Weaks的增加或减少,在底层中通过一条指令就可以实现,是原子操作。
类中封装了函数_Decref(),表示:若 引用计数 _Uses == 0,就调用_Destroy()来释放资源,并调用_Decwref()递减_Weaks。
类中封装了函数_Decwref(),表示:若弱引用计数_Weaks= =0,就调用_Delete_this()来删除计数器自身。
由_Ref_count_base类的源码可知,释放资源的条件为: _Uses== 0;删除计数器的条件为:_Uses== 0&&_Weaks==0。1.1、派生类_Ref_count
template<class _Ty> class _Ref_count : public _Ref_count_base { // handle reference counting for object without deleter public: _Ref_count(_Ty *_Px) : _Ref_count_base(), _Ptr(_Px) { // construct } private: virtual void _Destroy() { // destroy managed resource delete _Ptr; } virtual void _Delete_this() { // destroy self delete this; } _Ty * _Ptr; };
Ref_count是_Ref_count_base的派生类,定义了资源释放函数_Destroy() 、删除计数器自身函数_Delete_this(),以及一个指向_Ty类型的指针_Ptr,由派生类_Ref_count生成对象时只能通过指针_Ptr来进行构造。
1.2、派生类_Ref_count_del
template<class _Ty, class _Dx> class _Ref_count_del : public _Ref_count_base { // handle reference counting for object with deleter public: _Ref_count_del(_Ty *_Px, _Dx _Dt) : _Ref_count_base(), _Ptr(_Px), _Dtor(_Dt) { // construct } virtual void *_Get_deleter(const _XSTD2 type_info& _Typeid) const { // return address of deleter object return ((void *)(_Typeid == typeid(_Dx) ? &_Dtor : 0)); } private: virtual void _Destroy() { // destroy managed resource _Dtor(_Ptr); } virtual void _Delete_this() { // destroy self delete this; } _Ty * _Ptr; _Dx _Dtor; // the stored destructor for the controlled object 被控制对象的已析构函数 };
Ref_count_del是_Ref_count_base的派生类,定义了资源释放函数_Destroy() 、删除计数器自身函数_Delete_this(),以及一个指向_Ty类型的指针_Ptr和_Dx类型的删除器_Dtor,由派生类_Ref_count生成对象时只能通过指针_Ptr和删除器_Dtor来进行构造。
1.3、派生类_Ref_count_del_alloc
template<class _Ty, class _Dx, class _Alloc> class _Ref_count_del_alloc : public _Ref_count_base { // handle reference counting for object with deleter and allocator public: typedef _Ref_count_del_alloc<_Ty, _Dx, _Alloc> _Myty; typedef typename _Alloc::template rebind<_Myty>::other _Myalty; _Ref_count_del_alloc(_Ty *_Px, _Dx _Dt, _Myalty _Al) : _Ref_count_base(), _Ptr(_Px), _Dtor(_Dt), _Myal(_Al) { // construct } virtual void *_Get_deleter(const _XSTD2 type_info& _Typeid) const { // return address of deleter object return ((void *)(_Typeid == typeid(_Dx) ? &_Dtor : 0)); } private: virtual void _Destroy() { // destroy managed resource _Dtor(_Ptr); } virtual void _Delete_this() { // destroy self _Myalty _Al = _Myal; _Al.destroy(this); _Al.deallocate(this, 1); } _Ty * _Ptr; _Dx _Dtor; // the stored destructor for the controlled object _Myalty _Myal; // the stored allocator for this };
Ref_count_del_alloc是_Ref_count_base的派生类,定义了资源释放函数_Destroy() 、删除计数器自身函数_Delete_this(),以及一个指向_Ty类型的指针_Ptr、_Dx类型的删除器_Dtor、_Myalty类型的分配器_Myal,由派生类_Ref_count生成对象时只能通过指针_Ptr、删除器_Dtor和分配器_Myal来进行构造。
2、基类_Ptr_base
基类_Ptr_base中含有两个成员变量,一个指向_Ty类型的指针_Ptr、一个指向_Ref_count_base类型的计数器指针_Rep。此处的指针_Ptr应与类_Ref_count_base下派生类中的_Ptr相同。
_Ptr_base中的主要函数如下:_Ptr_base() //无参构造函数 _Ptr_base(_Myt&& _Right) //移动构造函数 template<class _Ty2> _Ptr_base(_Ptr_base<_Ty2>&& _Right) //移动构造函数 _Myt& operator=(_Myt&& _Right) //移动赋值函数 long use_count() const _NOEXCEPT //获取强引用计数 void _Decref() //调用_Rep->_Decref(),减少计数器的强引用计数 void _Decwref() //调用_Rep->_Decwref(),减少计数器的弱引用计数 void _Reset()及其重载版本 //重置当前shared_ptr,减少当前对象持有某资源的强引用计数,增加传入对象持有新资源的强引用计数 void _Resetw()及其重载版本 //重置当前weak_ptr,减少当前对象持有某资源的弱引用计数,增加传入对象持有新资源的弱引用计数
3、shared_ptr
shared_ptr是基类_Ptr_base的派生类,也是用户生成共享智能指针的接口。shared_ptr封装了大量的构造函数、赋值函数,主要供用户构造对象使用:
传参构造函数
shared_ptr() _NOEXCEPT //无参构造函数,调用基类默认构造函数,使_Ptr和_Rep均初始化为0 template<class _Ux> explicit shared_ptr(_Ux *_Px) //用资源指针_Px构造,调用_Ref_count,构造完成后强引用计数为1 template<class _Ux,class _Dx> shared_ptr(_Ux *_Px, _Dx _Dt)//用资源指针和删除器来构造,调用_Ref_count_del,构造完成后强引用计数为1 template<class _Dx> shared_ptr(nullptr_t, _Dx _Dt) //用nullptr和删除器来构造,调用_Ref_count_del,构造完成后强引用计数为1 template<class _Dx,class _Alloc> shared_ptr(nullptr_t, _Dx _Dt, _Alloc _Ax) //用nullptr、删除器、分配器来构造,调用_Ref_count_del_alloc,构造完成后强引用计数为1 template<class _Ux,class _Dx,class _Alloc> shared_ptr(_Ux *_Px, _Dx _Dt, _Alloc _Ax) //用不同类型的资源指针、删除器及分配器来构造,调用_Ref_count_del_alloc,构造完成后强引用计数为1
以上构造函数为传参构造函数:用于从无到有构造一个shared_ptr对象,同时生成一个计数器对象,构造完成后的引用计数和弱引用计数均为1。
拷贝构造函数
template<class _Ty2> shared_ptr(const shared_ptr<_Ty2>& _Right, _Ty *_Px) _NOEXCEPT //传入shared_ptr对象和新的资源指针对构造对象,_Right的引用计数不会改变 shared_ptr(const _Myt& _Other) _NOEXCEPT //拷贝构造,传入shared_ptr对象,当前对象绑定_Other的资源指针和计数器,引用计数+1 template<class _Ty2> explicit shared_ptr(const weak_ptr<_Ty2>& _Other,bool _Throw = true) //用weak_ptr对象来构造shared_ptr,若成功,二者共享资源指针以及计数器,强引用计数+1,否则抛出异常 template<class _Ty2> shared_ptr(const shared_ptr<_Ty2>& _Other, const _Const_tag& _Tag) //传入shared_ptr对象来构造,第二个参数指定使用_Const_tag转换 template<class _Ty2> shared_ptr(const shared_ptr<_Ty2>& _Other, const _Dynamic_tag& _Tag) 传入shared_ptr对象来构造,第二个参数指定使用Dynamic_tag转换
以上构造函数均为拷贝构造函数:均调用_Ptr_base中的_Reset函数,即用现有的对象来构造新的对象,共享资源和计数器,且构造完成后强引用计数+1。
移动构造函数
template<class _Ty2> shared_ptr(auto_ptr<_Ty2>&& _Other) //传入auto_ptr临时对象,构造完成后,临时对象销毁 shared_ptr(_Myt&& _Right) _NOEXCEPT //传入shared_ptr临时对象,构造完成后,临时对象销毁 template<class _Ty2, class = typename enable_if<is_convertible<_Ty2 *, _Ty *>::value,void>::type> shared_ptr(shared_ptr<_Ty2>&& _Right) _NOEXCEPT //传入不同类型的shared_ptr临时对象,构造完成后,临时对象销毁 template<class _Ux,class _Dx> shared_ptr(unique_ptr<_Ux, _Dx>&& _Right) 传入unique_ptr临时对象,获取其资源指针以及删除器,并将_Right的资源指针设置为NULL
以上构造函数均为移动构造函数:用一个临时对象来构造新的对象,继承其资源和计数器,构造完成销毁临时对象,故引用计数不变。
赋值函数
template<class _Ux,class _Dx> _Myt& operator=(unique_ptr<_Ux, _Dx>&& _Right) //传入unique_ptr临时对象,赋值给当前shared_ptr对象,_Right的引用计数不变,自身的引用计数-1 _Myt& operator=(_Myt&& _Right) _NOEXCEPT //传入shared_ptr临时对象,赋值完成后,_Right的引用计数不变,当前对象的引用计数-1 template<class _Ty2> _Myt& operator=(shared_ptr<_Ty2>&& _Right) _NOEXCEPT //传入不同类型的shared_ptr临时对象,赋值完成后,临时对象销毁 _Myt& operator=(const _Myt& _Right) _NOEXCEPT //传入shared_ptr对象,可以是临时对象也可以是非临时对象,_Right的引用计数+1,当前对象的引用计数-1 template<class _Ty2> _Myt& operator=(const shared_ptr<_Ty2>& _Right) _NOEXCEPT //传入不同类型的shared_ptr对象,_Right的引用计数+1,当前对象的引用计数-1 template<class _Ty2> _Myt& operator=(auto_ptr<_Ty2>&& _Right) //传入不同类型的auto_ptr对象,_Right的引用计数不变,当前对象的计数器引用计数-1
由上述赋值函数可知,若为移动赋值函数,传入的对象为临时对象,赋值完成后,临时对象持有资源的引用计数不发生不变化(移动对象std::move),当前对象持有资源的引用计数-1;若为拷贝赋值函数,赋值完成后,传入对象的引用计数+1,当前对象持有资源的引用计数-1。
析构函数
~shared_ptr() _NOEXCEPT { // release resource this->_Decref(); //如果引用计数为0,则在计数器类中进行资源释放 }
某个shared_ptr生命期结束时,会自动调用析构函数,析构函数中会调用计数器的_Decref()函数,资源的强引用计数-1,若强引用计数为0,就会调用_Destroy() 函数释放资源。
综上所述,智能指针shared_ptr的原理为:所有shared_ptr对象中通过同一个计数器来计算资源的引用计数_Uses,可通过拷贝构造函数、移动构造函数、赋值函数来增加新的shared_ptr指向该对象,并自动调用计数器递增引用计数。当某个shared_ptr生命期结束时,会自动调用析构函数,析构函数中会递减它所指向对象的引用计数,若强引用计数为0,析构函数就会调用计数器的_Destroy() 函数释放内存资源。
shared_ptr线程安全
(1)shared_ptr对象的线程安全: 同一个shared_ptr对象被多个线程写不是线程安全的,需要加锁;
(2)计数器的线程安全: 共享引用计数的不同的shared_ptr对象,其引用计数的增加或减少被多个线程写是线程安全的,因为引用计数的增加或减小是原子操作。shared_ptr应用场景
(1)多个对象共享同一个资源,对象指向资源的创建与销毁是分离的;
(2)容器中存储动态对象。 假设程序中定义一个容器为vector<int*> v;int * p=new int(4);v.push_back( p);,当程序结束对应容器的生命期也结束时,vector容器会发生析构从而释放其中的元素p,但是并不会释放p所指向的内存资源,就会造成内存泄露。当将容器中的指针元素换为shared_ptr时,即vector<shared_ptr< int>>,当vector生命期结束时,析构容器中的元素,而shared_ptr对象可以自动释放其管理的内存资源,就不会发生内存泄露。
(3)管理动态数组。4、weak_ptr
weak_ptr也是基类_Ptr_base的派生类,是用户生成弱共享智能指针的接口。weak_ptr的定义比shared_ptr简单很多。
weak_ptr的构造函数、赋值函数与shared_ptr类似,但是只能通过其它weak_ptr、shared_ptr来构造,在此不再重复。
weak_ptr中没有重载->操作符,无法获取资源指针,无法访问资源的内存空间,只能判断资源是否有效;
weak_ptr对象与shared_ptr对象共享一个计数器对象,计数器对象在构造第一个shared_ptr对象时生成;
weak_ptr利用expired函数检查资源是否有效,若返回true(即引用计数为0),表示资源已经释放,否则,至少有一个shared_ptr对象持有资源。
weak_ptr不能直接访问资源,而必须调用lock函数检查资源是否被释放,并返回一个shared_ptr对象。若资源已被释放(即expired为true),返回的是一个空shared_ptr对象;否则,返回一个shared_tr,引用计数+1。
同shared_ptr一样,weak_ptr的生命期结束时,会自动调用析构函数,析构函数中会调用计数器的_Decwref()函数,资源的弱引用计数-1,若弱引用计数为0,就会调用_Delete_this() 函数删除计数器对象。
综上所述,智能指针weak_ptr的原理为:weak_ptr对象依赖shared_ptr/weak_ptr对象生成,并自动调用计数器递增弱引用计数。当某个weak_ptr生命期结束时,会自动调用析构函数,析构函数中会通过计数器递减弱引用计数,若弱引用计数为0,析构函数就会调用计数器的_Delete_this()函数删除计数器对象。
不知道大家有没有注意到一个问题,为什么在计数器初始化的时候就要将弱引用计数设置为1呢?
若弱引用计数初始化为0,在强引用计数不为0的情况下,weak_ptr对象生命期都结束时,此时弱引用计数为0,就会删除计数器,但这时share_ptr对象尚在使用、资源也未释放,就会出现内存错误。5、shared_ptr使用注意事项
在使用shared_ptr指针的过程中,一定要注意避免智能指针的循环引用从而导致的内存泄露,而解决这个问题的方法就是在可能出现循环引用的地方使用weak_ptr来替代shared_ptr。
举例说明:(以下内容来自博客:关于shared_ptr与weak_ptr的使用。)#include <iostream> #include <boost/smart_ptr.hpp> using namespace std; using namespace boost; class BB; class AA { public: AA() { cout << "AA::AA() called" << endl; } ~AA() { cout << "AA::~AA() called" << endl; } shared_ptr<BB> m_bb_ptr; //! }; class BB { public: BB() { cout << "BB::BB() called" << endl; } ~BB() { cout << "BB::~BB() called" << endl; } shared_ptr<AA> m_aa_ptr; //! }; int main() { shared_ptr<AA> ptr_a (new AA); shared_ptr<BB> ptr_b ( new BB); cout << "ptr_a use_count: " << ptr_a.use_count() << endl; cout << "ptr_b use_count: " << ptr_b.use_count() << endl; //下面两句导致了AA与BB的循环引用,结果就是AA和BB对象都不会析构 ptr_a->m_bb_ptr = ptr_b; ptr_b->m_aa_ptr = ptr_a; cout << "ptr_a use_count: " << ptr_a.use_count() << endl; cout << "ptr_b use_count: " << ptr_b.use_count() << endl; }
运行结果:
由结果可知:由于类A和B中内部的成员变量shared_ptr各自保存了对方的一次引用,使得shared_ptr对象ptr_a和ptr_b各自的引用计数均为2,程序结束时两者的引用计数均-1但都不为0,因此不会释放各自持有的内存资源,即A和B的析构函数不会被调用,因此就发生了内存泄露。出现循环引用的解决办法就是将其中一个shared_ptr改为weak_ptr,对应代码如下:
#include <iostream> #include <boost/smart_ptr.hpp> using namespace std; using namespace boost; class BB; class AA { public: AA() { cout << "AA::AA() called" << endl; } ~AA() { cout << "AA::~AA() called" << endl; } weak_ptr<BB> m_bb_ptr; //! }; class BB { public: BB() { cout << "BB::BB() called" << endl; } ~BB() { cout << "BB::~BB() called" << endl; } shared_ptr<AA> m_aa_ptr; //! }; int main() { shared_ptr<AA> ptr_a (new AA); shared_ptr<BB> ptr_b ( new BB); cout << "ptr_a use_count: " << ptr_a.use_count() << endl; cout << "ptr_b use_count: " << ptr_b.use_count() << endl; //下面两句导致了AA与BB的循环引用,结果就是AA和BB对象都不会析构 ptr_a->m_bb_ptr = ptr_b; ptr_b->m_aa_ptr = ptr_a; cout << "ptr_a use_count: " << ptr_a.use_count() << endl; cout << "ptr_b use_count: " << ptr_b.use_count() << endl; }
运行结果:
从结果可以看到ptr_a的引用计数为2,而ptr_b的引用计数为1,这是因为weak_ptr不会改变强引用计数,因此在程序结束时ptr_a与ptr_b的引用计数均会-1,此时ptr_b的引用计数变为0,就会释放其持有的内存资源即BB发生析构,而BB析构时又会释放其成员变量m_aa_ptr就会使ptr_a的引用计数-1变为0,从而ptr_a就会释放其持有的资源,即AA发生析构。由此可见,此时不会发生内存泄露。参考博客:share_ptr与weak_ptr的区别与联系。
-
C++智能指针 shared_ptr 与 weak_ptr 原理
2017-12-12 15:46:56注:源代码摘自 GNU C++,除此之外为原创,转载请注明出处。 ...// // This file is part of the GNU ISO C++ Library. 一、weak_ptr 的 lock() 函数原理/* 当每次有新的 shared_ptr 生成时,会增加 _Sp_- 注:源代码摘自 GNU C++,除此之外为原创,转载请注明出处。
欢迎关注 [懒人漫说] 公众号,分享Java、Android、C/C++ 技术,
包括基础、自己遇到的问题解决过程。
当然如果关注并留言问题的话,我们力所能及的话会帮你解决并回复哟。我们和你一样,是正在成长的程序员,我们也会分享自己的成长路上的感想,希望可以和你一起努力成长。// Copyright (C) 2007-2016 Free Software Foundation, Inc. // // This file is part of the GNU ISO C++ Library.
一、weak_ptr 的 lock() 函数原理
/* 当每次有新的 shared_ptr 生成时,会增加 _Sp_counted_base 的 _M_use_count (+1); 当每次有新的 weak_ptr 生成时,会增加 _Sp_counted_base 的 _M_weak_count (+1); 通过weak_ptr<x>::lock()函数可以获取x对象的强引用(shared_ptr)。 对应的,shared_ptr 析构时,会调用 _Sp_counted_base 的 _M_release 来使 _M_use_count (-1); weak_ptr 析构时,会调用 _Sp_counted_base 的 _M_release 来使 _M_weak_count (-1); 当 _M_use_count 的数量为 0 的时候 就会释放,原始的对象(用户自定义对象) 的内存,但是不会释放 _Sp_counted_base 的内存。 当 _M_use_count 和 _M_weak_count 都为 0的时候, 才会释放 _Sp_counted_base 的内存。 所以将一个普通指针赋值给两个不同的shared_ptr对象会引发double free。 eg: int *n = new int(2); shared_ptr<int> pn1(n); shared_ptr<int> pn2(n); // pn1 和 pn2 内部保存的指针都是n,而且内部引用计数都为1,所以在离开作用域的时候两个指针都会发生析构并释放指向的内存n。 尽量使用 shared_ptr<int> pn = make_shared<int> (2); 这种形式初始化一个shared_ptr指针。 */ /* * 此处引用 《Linux 多线程 服务端编程 使用 muduo C++ 网络库》 中对这两个智能指针的描述: * 1 shared_ptr 控制对象的生命期。shared_ptr 是强引用,只要有一个指向x对象的shared_ptr存在,该x对象就不会 * 析构。当指向对象x的最后一个shared_ptr析构或reset()的时候,x保证会被销毁。 * 2 weak_ptr 不控制对象的生命期,但是它知道对象是否还活着。如果对象还活着,那么它可以提升(promote)为有效的 * shared_ptr; 如果对象已经死了,提升会失败,返回一个空的shared_ptr。“提升/lock()” 行为是线程安全的。 * 3 shared_ptr/weak_ptr 的“计数”在主流平台上是原子操作,没有用锁,性能不俗。 * 4 shared_ptr/weak_ptr 的线程安全级别与 std::string和STL容器一样。 */ // __shared_count的构造函数会 new 一个 计数对象(_Sp_counted_base),但是__weak_count的构造函数不会new。 __shared_count(_Ptr __p) : _M_pi(0) { __try { typedef typename std::tr1::remove_pointer<_Ptr>::type _Tp; _M_pi = new _Sp_counted_base_impl<_Ptr, _Sp_deleter<_Tp>, _Lp>( __p, _Sp_deleter<_Tp>()); } __catch(...) { delete __p; __throw_exception_again; } } // lock 函数 __shared_ptr<_Tp, _Lp> lock() const // never throws { #ifdef __GTHREADS // Optimization: avoid throw overhead. if (expired()) return __shared_ptr<element_type, _lp = "">(); __try { return __shared_ptr<element_type, _lp = "">(*this); } __catch(const bad_weak_ptr&) { return __shared_ptr<element_type, _lp = "">(); } #else // Optimization: avoid try/catch overhead when single threaded. return expired() ? __shared_ptr<element_type, _lp = "">() : __shared_ptr<element_type, _lp = "">(*this); #endif } // XXX MT bool expired() const // never throws { return _M_refcount._M_get_use_count() == 0; }
二、类成员图:
三、下面源码有点长,不差资源分的话可以点击链接下载。
*注:所有的源码参看连接: http://download.csdn.net/download/u013005025/10155393四、源码 (//version: Copyright © 2007-2016 Free Software Foundation, Inc.)
1 weak_ptr 源码
// weak_ptr template<typename _tp = ""> class weak_ptr : public __weak_ptr < _Tp > { public: weak_ptr() : __weak_ptr<_Tp>() { } template<typename _tp1 = ""> weak_ptr(const weak_ptr<_Tp1>& __r) : __weak_ptr<_Tp>(__r) { } template<typename _tp1 = ""> weak_ptr(const shared_ptr<_Tp1>& __r) : __weak_ptr<_Tp>(__r) { } template<typename _tp1 = ""> weak_ptr& operator=(const weak_ptr<_Tp1>& __r) // never throws { this->__weak_ptr<_Tp>::operator=(__r); return *this; } template<typename _tp1 = ""> weak_ptr& operator=(const shared_ptr<_Tp1>& __r) // never throws { this->__weak_ptr<_Tp>::operator=(__r); return *this; } shared_ptr<_Tp> lock() const // never throws { #ifdef __GTHREADS if (this->expired()) return shared_ptr<_Tp>(); __try{ return shared_ptr<_Tp>(*this); }__catch(const bad_weak_ptr&){ return shared_ptr<_Tp>(); } #else return this->expired() ? shared_ptr<_Tp>() : shared_ptr<_Tp>(*this); #endif } };
2 __weak_ptr 源码
template<typename _lock_policy = "" _lp = ""> class __weak_ptr { public: typedef _Tp element_type; __weak_ptr() : _M_ptr(0), _M_refcount() // never throws { } template<typename _tp1 = ""> __weak_ptr(const __weak_ptr<_Tp1, _Lp>& __r) : _M_refcount(__r._M_refcount) // never throws { __glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>) _M_ptr = __r.lock().get(); } template<typename _tp1 = ""> __weak_ptr(const __shared_ptr<_Tp1, _Lp>& __r) : _M_ptr(__r._M_ptr), _M_refcount(__r._M_refcount) // never throws { __glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>) } template<typename _tp1 = ""> __weak_ptr& operator=(const __weak_ptr<_Tp1, _Lp>& __r) // never throws { _M_ptr = __r.lock().get(); _M_refcount = __r._M_refcount; return *this; } template<typename _tp1 = ""> __weak_ptr& operator=(const __shared_ptr<_Tp1, _Lp>& __r) // never throws { _M_ptr = __r._M_ptr; _M_refcount = __r._M_refcount; return *this; } __shared_ptr<_Tp, _Lp> lock() const // never throws { #ifdef __GTHREADS // Optimization: avoid throw overhead. if (expired()) return __shared_ptr<element_type, _lp = "">(); __try { return __shared_ptr<element_type, _lp = "">(*this); } __catch(const bad_weak_ptr&) { return __shared_ptr<element_type, _lp = "">(); } #else // Optimization: avoid try/catch overhead when single threaded. return expired() ? __shared_ptr<element_type, _lp = "">() : __shared_ptr<element_type, _lp = "">(*this); #endif } // XXX MT long use_count() const // never throws { return _M_refcount._M_get_use_count(); } bool expired() const // never throws { return _M_refcount._M_get_use_count() == 0; } void reset() // never throws { __weak_ptr().swap(*this); } void swap(__weak_ptr& __s) // never throws { 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) { _M_ptr = __ptr; _M_refcount = __refcount; } template<typename _tp1 = ""> bool _M_less(const __weak_ptr<_Tp1, _Lp>& __rhs) const { return _M_refcount < __rhs._M_refcount; } template<typename _lock_policy = "" _lp1 = ""> friend class __shared_ptr; template<typename _lock_policy = "" _lp1 = ""> friend class __weak_ptr; friend class __enable_shared_from_this < _Tp, _Lp > ; friend class enable_shared_from_this < _Tp > ; // Friend injected into namespace and found by ADL. template<typename _tp1 = ""> friend inline bool operator<(const __weak_ptr& __lhs, const __weak_ptr<_Tp1, _Lp>& __rhs) { return __lhs._M_less(__rhs); } _Tp* _M_ptr; // Contained pointer. __weak_count<_Lp> _M_refcount; // Reference counter. };
3 __weak_count 源码
template<_Lock_policy _Lp> class __weak_count { public: __weak_count() : _M_pi(0) // nothrow { } __weak_count(const __shared_count<_Lp>& __r) : _M_pi(__r._M_pi) // nothrow { if (_M_pi != 0) _M_pi->_M_weak_add_ref(); } __weak_count(const __weak_count<_Lp>& __r) : _M_pi(__r._M_pi) // nothrow { if (_M_pi != 0) _M_pi->_M_weak_add_ref(); } ~__weak_count() // nothrow { if (_M_pi != 0) _M_pi->_M_weak_release(); } __weak_count<_Lp>& operator=(const __shared_count<_Lp>& __r) // nothrow { _Sp_counted_base<_Lp>* __tmp = __r._M_pi; if (__tmp != 0) __tmp->_M_weak_add_ref(); if (_M_pi != 0) _M_pi->_M_weak_release(); _M_pi = __tmp; return *this; } __weak_count<_Lp>& operator=(const __weak_count<_Lp>& __r) // nothrow { _Sp_counted_base<_Lp>* __tmp = __r._M_pi; if (__tmp != 0) __tmp->_M_weak_add_ref(); if (_M_pi != 0) _M_pi->_M_weak_release(); _M_pi = __tmp; return *this; } void _M_swap(__weak_count<_Lp>& __r) // nothrow { _Sp_counted_base<_Lp>* __tmp = __r._M_pi; __r._M_pi = _M_pi; _M_pi = __tmp; } long _M_get_use_count() const // nothrow { return _M_pi != 0 ? _M_pi->_M_get_use_count() : 0; } friend inline bool operator==(const __weak_count<_Lp>& __a, const __weak_count<_Lp>& __b) { return __a._M_pi == __b._M_pi; } friend inline bool operator<(const __weak_count<_Lp>& __a, const __weak_count<_Lp>& __b) { return std::less<_Sp_counted_base<_Lp>*>()(__a._M_pi, __b._M_pi); } private: friend class __shared_count < _Lp > ; _Sp_counted_base<_Lp>* _M_pi; };
五、shared_ptr 源码
// The actual shared_ptr, with forwarding constructors and // assignment operators. // shared_ptr template<typename _tp = ""> class shared_ptr : public __shared_ptr < _Tp > { public: shared_ptr() : __shared_ptr<_Tp>() { } template<typename _tp1 = ""> explicit shared_ptr(_Tp1* __p) : __shared_ptr<_Tp>(__p) { } template<typename _deleter = "" typename = ""> shared_ptr(_Tp1* __p, _Deleter __d) : __shared_ptr<_Tp>(__p, __d) { } template<typename _tp1 = ""> shared_ptr(const shared_ptr<_Tp1>& __r) : __shared_ptr<_Tp>(__r) { } template<typename _tp1 = ""> explicit shared_ptr(const weak_ptr<_Tp1>& __r) : __shared_ptr<_Tp>(__r) { } #if (__cplusplus < 201103L) || _GLIBCXX_USE_DEPRECATED template<typename _tp1 = ""> explicit shared_ptr(std::auto_ptr<_Tp1>& __r) : __shared_ptr<_Tp>(__r) { } #endif template<typename _tp1 = ""> shared_ptr(const shared_ptr<_Tp1>& __r, __static_cast_tag) : __shared_ptr<_Tp>(__r, __static_cast_tag()) { } template<typename _tp1 = ""> shared_ptr(const shared_ptr<_Tp1>& __r, __const_cast_tag) : __shared_ptr<_Tp>(__r, __const_cast_tag()) { } template<typename _tp1 = ""> shared_ptr(const shared_ptr<_Tp1>& __r, __dynamic_cast_tag) : __shared_ptr<_Tp>(__r, __dynamic_cast_tag()) { } template<typename _tp1 = ""> shared_ptr& operator=(const shared_ptr<_Tp1>& __r) // never throws { this->__shared_ptr<_Tp>::operator=(__r); return *this; } #if (__cplusplus < 201103L) || _GLIBCXX_USE_DEPRECATED template<typename _tp1 = ""> shared_ptr& operator=(std::auto_ptr<_Tp1>& __r) { this->__shared_ptr<_Tp>::operator=(__r); return *this; } #endif }; // __shared_ptr struct __static_cast_tag { }; struct __const_cast_tag { }; struct __dynamic_cast_tag { }; // A smart pointer with reference-counted copy semantics. The // object pointed to is deleted when the last shared_ptr pointing to // it is destroyed or reset. template<typename _lock_policy = "" _lp = ""> class __shared_ptr { public: typedef _Tp element_type; __shared_ptr() : _M_ptr(0), _M_refcount() // never throws { } template<typename _tp1 = ""> explicit __shared_ptr(_Tp1* __p) : _M_ptr(__p), _M_refcount(__p) { __glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>) typedef int _IsComplete[sizeof(_Tp1)]; __enable_shared_from_this_helper(_M_refcount, __p, __p); } template<typename _deleter = "" typename = ""> __shared_ptr(_Tp1* __p, _Deleter __d) : _M_ptr(__p), _M_refcount(__p, __d) { __glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>) // TODO requires _Deleter CopyConstructible and __d(__p) well-formed __enable_shared_from_this_helper(_M_refcount, __p, __p); } // generated copy constructor, assignment, destructor are fine. template<typename _tp1 = ""> __shared_ptr(const __shared_ptr<_Tp1, _Lp>& __r) : _M_ptr(__r._M_ptr), _M_refcount(__r._M_refcount) // never throws { __glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>) } template<typename _tp1 = ""> explicit __shared_ptr(const __weak_ptr<_Tp1, _Lp>& __r) : _M_refcount(__r._M_refcount) // may throw { __glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>) // It is now safe to copy __r._M_ptr, as _M_refcount(__r._M_refcount) // did not throw. _M_ptr = __r._M_ptr; } #if (__cplusplus < 201103L) || _GLIBCXX_USE_DEPRECATED // Postcondition: use_count() == 1 and __r.get() == 0 template<typename _tp1 = ""> explicit __shared_ptr(std::auto_ptr<_Tp1>& __r) : _M_ptr(__r.get()), _M_refcount() { // TODO requries delete __r.release() well-formed __glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>) typedef int _IsComplete[sizeof(_Tp1)]; _Tp1* __tmp = __r.get(); _M_refcount = __shared_count<_Lp>(__r); __enable_shared_from_this_helper(_M_refcount, __tmp, __tmp); } #endif template<typename _tp1 = ""> __shared_ptr(const __shared_ptr<_Tp1, _Lp>& __r, __static_cast_tag) : _M_ptr(static_cast<element_type*>(__r._M_ptr)), _M_refcount(__r._M_refcount) { } template<typename _tp1 = ""> __shared_ptr(const __shared_ptr<_Tp1, _Lp>& __r, __const_cast_tag) : _M_ptr(const_cast<element_type*>(__r._M_ptr)), _M_refcount(__r._M_refcount) { } template<typename _tp1 = ""> __shared_ptr(const __shared_ptr<_Tp1, _Lp>& __r, __dynamic_cast_tag) : _M_ptr(dynamic_cast<element_type*>(__r._M_ptr)), _M_refcount(__r._M_refcount) { if (_M_ptr == 0) // need to allocate new counter -- the cast failed _M_refcount = __shared_count<_Lp>(); } template<typename _tp1 = ""> __shared_ptr& operator=(const __shared_ptr<_Tp1, _Lp>& __r) // never throws { _M_ptr = __r._M_ptr; _M_refcount = __r._M_refcount; // __shared_count::op= doesn't throw return *this; } #if (__cplusplus < 201103L) || _GLIBCXX_USE_DEPRECATED template<typename _tp1 = ""> __shared_ptr& operator=(std::auto_ptr<_Tp1>& __r) { __shared_ptr(__r).swap(*this); return *this; } #endif void reset() // never throws { __shared_ptr().swap(*this); } template<typename _tp1 = ""> void reset(_Tp1* __p) // _Tp1 must be complete. { // Catch self-reset errors. _GLIBCXX_DEBUG_ASSERT(__p == 0 || __p != _M_ptr); __shared_ptr(__p).swap(*this); } template<typename _deleter = "" typename = ""> void reset(_Tp1* __p, _Deleter __d) { __shared_ptr(__p, __d).swap(*this); } // Allow class instantiation when _Tp is [cv-qual] void. typename std::tr1::add_reference<_Tp>::type operator*() const // never throws { _GLIBCXX_DEBUG_ASSERT(_M_ptr != 0); return *_M_ptr; } _Tp* operator->() const // never throws { _GLIBCXX_DEBUG_ASSERT(_M_ptr != 0); return _M_ptr; } _Tp* get() const // never throws { return _M_ptr; } // Implicit conversion to "bool" private: typedef _Tp* __shared_ptr::*__unspecified_bool_type; public: operator __unspecified_bool_type() const // never throws { return _M_ptr == 0 ? 0 : &__shared_ptr::_M_ptr; } bool unique() const // never throws { return _M_refcount._M_unique(); } long use_count() const // never throws { return _M_refcount._M_get_use_count(); } void swap(__shared_ptr<_Tp, _Lp>& __other) // never throws { std::swap(_M_ptr, __other._M_ptr); _M_refcount._M_swap(__other._M_refcount); } private: void* _M_get_deleter(const std::type_info& __ti) const { return _M_refcount._M_get_deleter(__ti); } template<typename _lock_policy = "" _lp1 = ""> bool _M_less(const __shared_ptr<_Tp1, _Lp1>& __rhs) const { return _M_refcount < __rhs._M_refcount; } template<typename _lock_policy = "" _lp1 = ""> friend class __shared_ptr; template<typename _lock_policy = "" _lp1 = ""> friend class __weak_ptr; template<typename _lock_policy = "" _lp1 = "" typename = ""> friend _Del* get_deleter(const __shared_ptr<_Tp1, _Lp1>&); // Friends injected into enclosing namespace and found by ADL: template<typename _tp1 = ""> friend inline bool operator==(const __shared_ptr& __a, const __shared_ptr<_Tp1, _Lp>& __b) { return __a.get() == __b.get(); } template<typename _tp1 = ""> friend inline bool operator!=(const __shared_ptr& __a, const __shared_ptr<_Tp1, _Lp>& __b) { return __a.get() != __b.get(); } template<typename _tp1 = ""> friend inline bool operator<(const __shared_ptr& __a, const __shared_ptr<_Tp1, _Lp>& __b) { return __a._M_less(__b); } _Tp* _M_ptr; // Contained pointer. __shared_count<_Lp> _M_refcount; // Reference counter. }; // __shared_count template<_Lock_policy _Lp = __default_lock_policy> class __shared_count { public: __shared_count() : _M_pi(0) // nothrow { } template<typename _ptr = ""> __shared_count(_Ptr __p) : _M_pi(0) { __try { typedef typename std::tr1::remove_pointer<_Ptr>::type _Tp; _M_pi = new _Sp_counted_base_impl<_Ptr, _Sp_deleter<_Tp>, _Lp>( __p, _Sp_deleter<_Tp>()); } __catch(...) { delete __p; __throw_exception_again; } } template<typename _deleter = "" typename = ""> __shared_count(_Ptr __p, _Deleter __d) : _M_pi(0) { __try { _M_pi = new _Sp_counted_base_impl<_Ptr, _Deleter, _Lp>(__p, __d); } __catch(...) { __d(__p); // Call _Deleter on __p. __throw_exception_again; } } // Special case for auto_ptr<_Tp> to provide the strong guarantee. template<typename _tp = ""> explicit __shared_count(std::auto_ptr<_Tp>& __r) : _M_pi(new _Sp_counted_base_impl < _Tp*, _Sp_deleter<_Tp>, _Lp > (__r.get(), _Sp_deleter<_Tp>())) { __r.release(); } // Throw bad_weak_ptr when __r._M_get_use_count() == 0. explicit __shared_count(const __weak_count<_Lp>& __r); ~__shared_count() // nothrow { if (_M_pi != 0) _M_pi->_M_release(); } __shared_count(const __shared_count& __r) : _M_pi(__r._M_pi) // nothrow { if (_M_pi != 0) _M_pi->_M_add_ref_copy(); } __shared_count& operator=(const __shared_count& __r) // nothrow { _Sp_counted_base<_Lp>* __tmp = __r._M_pi; if (__tmp != _M_pi) { if (__tmp != 0) __tmp->_M_add_ref_copy(); if (_M_pi != 0) _M_pi->_M_release(); _M_pi = __tmp; } return *this; } void _M_swap(__shared_count& __r) // nothrow { _Sp_counted_base<_Lp>* __tmp = __r._M_pi; __r._M_pi = _M_pi; _M_pi = __tmp; } long _M_get_use_count() const // nothrow { return _M_pi != 0 ? _M_pi->_M_get_use_count() : 0; } bool _M_unique() const // nothrow { return this->_M_get_use_count() == 1; } friend inline bool operator==(const __shared_count& __a, const __shared_count& __b) { return __a._M_pi == __b._M_pi; } friend inline bool operator<(const __shared_count& __a, const __shared_count& __b) { return std::less<_Sp_counted_base<_Lp>*>()(__a._M_pi, __b._M_pi); } void* _M_get_deleter(const std::type_info& __ti) const { return _M_pi ? _M_pi->_M_get_deleter(__ti) : 0; } private: friend class __weak_count < _Lp > ; _Sp_counted_base<_Lp>* _M_pi; }; // _Sp_counted_base template<_Lock_policy _Lp = __default_lock_policy> class _Sp_counted_base : public _Mutex_base < _Lp > { public: _Sp_counted_base() : _M_use_count(1), _M_weak_count(1) { } virtual ~_Sp_counted_base() // nothrow { } // Called when _M_use_count drops to zero, to release the resources // managed by *this. virtual void _M_dispose() = 0; // nothrow // Called when _M_weak_count drops to zero. virtual void _M_destroy() // nothrow { delete this; } virtual void* _M_get_deleter(const std::type_info&) = 0; void _M_add_ref_copy() { __gnu_cxx::__atomic_add_dispatch(&_M_use_count, 1); } void _M_add_ref_lock(); void _M_release() // nothrow { // Be race-detector-friendly. For more info see bits/c++config. _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_use_count); if (__gnu_cxx::__exchange_and_add_dispatch(&_M_use_count, -1) == 1) { _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_use_count); _M_dispose(); // There must be a memory barrier between dispose() and destroy() // to ensure that the effects of dispose() are observed in the // thread that runs destroy(). // See https://gcc.gnu.org/ml/libstdc++/2005-11/msg00136.html if (_Mutex_base<_Lp>::_S_need_barriers) { __atomic_thread_fence(__ATOMIC_ACQ_REL); } // Be race-detector-friendly. For more info see bits/c++config. _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_weak_count); if (__gnu_cxx::__exchange_and_add_dispatch(&_M_weak_count, -1) == 1) { _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_weak_count); _M_destroy(); } } } void _M_weak_add_ref() // nothrow { __gnu_cxx::__atomic_add_dispatch(&_M_weak_count, 1); } void _M_weak_release() // nothrow { // Be race-detector-friendly. For more info see bits/c++config. _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_weak_count); if (__gnu_cxx::__exchange_and_add_dispatch(&_M_weak_count, -1) == 1) { _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_weak_count); if (_Mutex_base<_Lp>::_S_need_barriers) { // See _M_release(), // destroy() must observe results of dispose() __atomic_thread_fence(__ATOMIC_ACQ_REL); } _M_destroy(); } } long _M_get_use_count() const // nothrow { // No memory barrier is used here so there is no synchronization // with other threads. return const_cast<const volatile = "">(_M_use_count); } private: _Sp_counted_base(_Sp_counted_base const&); _Sp_counted_base& operator=(_Sp_counted_base const&); _Atomic_word _M_use_count; // #shared _Atomic_word _M_weak_count; // #weak + (#shared != 0) };
End ----------------------------------------------------------------------------------------------------
-
智能指针shared_ptr、unique_ptr、weak_ptr
2022-07-20 23:17:24本文记录自己对智能指针的理解,主要涉及shared_ptr,unique_ptr,weak_ptr三个常用的智能指针的引用,原理,遇到的问题展开。 -
shared_ptr, weak_ptr 原理及使用
2020-08-06 11:50:22#ifndef __SHARED_PTR_ #define __SHARED_PTR_ template <typename T> class shared_ptr { public: shared_ptr(T* p) : count(new int(1)), _ptr(p) {} shared_ptr(shared_ptr<T>& other) : ... -
C++智能指针的使用 shared_ptr weak_ptr unique_ptr
2022-01-02 22:16:55shared_ptr C++程序设计中使用堆内存是非常频繁的操作,堆内存的申请和释放都由程序员自己管理。...C++里面的四个智能指针: auto_ptr, unique_ptr,shared_ptr, weak_ptr 其中后三个是C++11支持, 并且第一个已 -
智能指针(三):weak_ptr浅析
2018-09-01 14:36:26weak_ptr这个指针天生一副“小弟”的模样,也是在C++11的时候引入的标准库,它的出现完全是为了弥补它老大shared_ptr天生有缺陷的问题,其实相比于上一代的智能指针auto_ptr来说,新进老大shared_ptr可以说近乎完美... -
share_ptr、weak_ptr、enable_shared_from_this理解
2022-04-17 11:54:09shared_ptr 继承体系和关键成员 由下图可知,一个shared_ptr对象,里面其实包含两个指针,一个指针指向被管理对象(manged object),一个指向管理对象(manager object),其中管理对象(manager object)中也包含... -
C++_智能指针shared_ptr、unique_ptr、weak_ptr、auto_ptr总结
2021-05-10 06:52:04但可以用在函数返回值中 unique_ptr可做为容器元素 用unique_ptr传递删除器 5 weak_ptr的原理 6 auto_ptr(C++11已放弃) 7 总结及源码位置 boost::scoped_ptr 参考: 参考资料:《C++ Primer中文版 第五版》 1 介绍... -
C++智能指针:auto_ptr、unique_ptr、shared_ptr、weak_ptr
2021-12-25 18:39:36智能指针:auto_ptr、unique_ptr、shared_ptr、weak_ptr -
【C++】weak_ptr弱引用智能指针详解
2020-05-03 15:34:11相比于上一代的智能指针auto_ptr来说,新进老大shared_ptr可以说近乎完美,但是通过引用计数实现的它,虽然解决了指针独占的问题,但也引来了引用成环的问题,这种问题靠它自己是没办法解决的,所以在C++11的时候将... -
智能指针(shared_ptr、unique_ptr、weak_ptr)的使用
2022-03-21 21:04:28C++中的智能指针实现是通过一个类来管理实际上的指针,这个类要具备指针的基本操作。 shared_ptr 创建一个智能指针 std::shared_ptr<int> ptra = std::make_shared<int>(20); std::shared_ptr<int&... -
c++智能指针的使用,shared_ptr,unique_ptr,weak_ptr
2022-01-12 06:11:14三种智能指针 shared_ptr,unique_ptr,weak_ptr; 将shared_ptr存放在一个容器中,不再需要它的时候,要erase掉。 allocator负责封装堆内存管理的对象,它们在整个标准库中使用,特别是STL容器使用它们来管理容器... -
C++智能指针详解[shared_ptr、unique_ptr、weak_ptr]]
2022-03-11 09:58:13C++智能指针、shared_ptr、unique_ptr、weak_ptr详解 -
c++智能指针 shared_ptr weak_ptr unique_ptr 语法 使用 实现
2020-11-22 14:43:16c++最开始设计没有考虑gc(garbage collection...shared_ptr原理是引用计数,用于指针需要被传递复制的时候 weak_ptr主要就是为了配合shared_ptr防止出现循环引用(两个对象各持有各自的shared_ptr, 都无法先释放), ... -
C++智能指针之shared _ptr、weak_ptr、unique_ptr
2019-12-15 22:43:24一、class shared_ptr shared_ptr具有共享式拥有的概念。即多个shared_ptr可以指向相同的对象,该对象和其相关资源会在“最后一个引用被销毁”时释放。 1、shared_ptr的初始化: (1)直接使用初始化 shared_... -
从源码看std::weak_ptr
2022-06-25 09:41:31weak_ptr -
C++智能指针总结三——shared_ptr与weak_ptr
2021-11-30 20:02:56为什么要使用shared_ptr3.shared_ptr的使用1.shared_ptr原理2.shared_ptr的使用 1.往期回顾 2.为什么要使用shared_ptr 这是因为,无论是auto_ptr还是unique_ptr都是排它型的,即只允许一个智能指针对象引用控制一块... -
c++ 智能指针 shared_ptr 和 weak_ptr
2022-02-06 17:23:41weak_ptr引入可以解决shared_ptr交叉引用时无法释放资源的问题。 示例代码: #include <iostream> #include <memory> using namespace std; class B; class A{ public: A(){cout << "A ... -
C++智能指针weak_ptr详解
2020-07-30 22:18:55但是通过引用计数实现的它,虽然解决了指针独占的问题,但也引来了引用成环的问题,这种问题靠它自己是没办法解决的,所以在C++11的时候将shared_ptr和weak_ptr一起引入了标准库,用来解决循环引用的问题。... -
C++——智能指针(shared_ptr、unique_ptr、weak_ptr)
2020-08-13 18:42:46三种智能指针:shared_ptr、unique_ptr、weak_ptr,均定义在memory头文件中。 1、shared_ptr 定义: shared_ptr允许多个对象指向同一个对象,是一个标准的共享所有权的智能指针,C++11引入到C++ STL 用法: -
C++智能指针之shared_ptr、unique_ptr、weak_ptr
2020-02-19 11:30:23#include <iostream> #include <vector> #include <memory> using namespace std;... 标准库还定义了一种名为weak_ptr的伴随类,它是一种弱引用,指向shared_ptr所管理的对象... -
C++11智能指针(unique_ptr、shared_ptr、weak_ptr)
2019-07-11 23:31:15更多文章欢迎访问 程序员小非 博客 很多人怕写C/C++ 程序就是因为指针,因为指针给了程序员高度的自由,同样也赋予了高度的责任,稍有不慎就...C++ 11中定义了unique_ptr、shared_ptr与weak_ptr三种智能指针(smart ... -
C++(20)——弱引用智能指针weak_ptr
2022-03-12 09:38:47在正式介绍weak_ptr之前,我们先来回忆一下shard_ptr的一些知识,我们直到shared_ptr是采用引用计数的智能指针,多个shared_ptr实例可以指向同一个对象,并维护了一个共享的引用计数器。 深入 weak_ptr也是一个引用... -
stl中auto_ptr,unique_ptr,shared_ptr,weak_ptr四种智能指针使用
2019-10-21 16:59:371. auto_ptr auto_ptr主要是用来解决资源自动释放的问题,比如如下代码: void Function() { Obj*p = new Obj(20); ... if (error occor) throw ... 或者 retrun; delete p; } 在函数遇到错误之后,一般会抛异常... -
C++智能指针3——弱指针weak_ptr详解
2020-01-30 11:21:04共享指针shared_ptr指针存在的一些问题可以有弱指针weak_ptr解决。