shared_ptr原理分析及实现
来源:互联网 发布:人工智能机器人的应用 编辑:程序博客网 时间:2024/05/23 22:02
原理分析
内存管理历来是C++编程的一项需要小心费力气的活,因为C++本身不带GC机制,所有的内存管理都需要我们手动实现,从malloc / free
到new / delete
,再到allocator
的出现,无非都是为了更合理简单的避免内存泄露。
指针本身是一个用法十分灵活并且功能强大的工具,然而它对内存的直接掌控也使得它不得不常常背起内存泄露的黑锅,因为忘记删除指针或者将一个指针删除两次的错误往往十分隐蔽,难以侦查。为了解决这个问题,boost
中的智能指针终于还是在C++11中被纳入了标准库。
来看个例子:
std::string* stringPtr1 = new std::string;
std::string* stringPtr2 = new std::string[100];
… // do something
delete stringptr1; // 删除一个对象
delete[] stringPtr2; // 删除一个由对象组成的数组
看上去一切都很好:new
出来的两个对象在完成任务后都被delete
掉了。然而考虑在do something
的时候发生点什么,比如抛出异常,程序跳转,或者函数返回了,那么会发生什么呢?显然,这两个delete
并不会得到执行,也就是说,内存泄露了。假如把上面的指针定义成智能指针,那么一切问题都将烟消云散,如下。
shared_ptr stringPtr1(new std::string);
shared_ptr stringPtr2 = new std::string[100];
… // do something
那么智能指针是如何帮助我们管理内存的呢?事实上,智能指针就是一个作用是资源管理的类,它是你在堆栈上声明的类模板,并可通过使用指向某个堆分配的对象的原始指针进行初始化(RAII
)。
在初始化智能指针后,它将拥有原始指针,这意味着智能指针负责删除原始指针指定的内存, 智能指针析构函数包括要删除的调用,当智能指针超出范围时将调用其析构函数,析构函数会自动释放资源。
现代C++中的三种智能指针简单介绍如下(引自MSDN智能指针(现代 C++)):
- unique_ptr
只允许基础指针的一个所有者。 除非你确信需要 shared_ptr,否则请将该指针用作 POCO 的默认选项。 可以移到新所有者,但不会复制或共享。 替换已弃用的 auto_ptr。 与 boost::scoped_ptr 比较。 unique_ptr 小巧高效,大小等同于一个指针且支持 rvalue 引用,从而可实现快速插入和对 STL 集合的检索。- shared_ptr
采用引用计数的智能指针。 如果你想要将一个原始指针分配给多个所有者(例如,从容器返回了指针副本又想保留原始指针时),请使用该指针。 直至所有 shared_ptr 所有者超出了范围或放弃所有权,才会删除原始指针。 大小为两个指针:一个用于对象,另一个用于包含引用计数的共享控制块。- weak_ptr
结合 shared_ptr 使用的特例智能指针。 weak_ptr 提供对一个或多个 shared_ptr 实例拥有的对象的访问,但不参与引用计数。 如果你想要观察某个对象但不需要其保持活动状态,请使用该实例。 在某些情况下,用于断开 shared_ptr 实例间的循环引用。
shared_ptr 解析
shared_ptr
的原理是引用计数法reference counting
,每多一个智能指针指向同一个对象时,引用+1
,而析构则相反,如果计数为零,则保存的指针被删除。
正常的指针的功能智能指针都有,如(以下用mySmartPtr
代替shared_ptr
,这是自己实现的智能指针名字)
int *pi = new int(42); mySmartPtr<int> *hpa(new mySmartPtr<int>(pi)); // 构造函数 mySmartPtr<int> *hpb = new mySmartPtr<int>(*hpa); // 拷贝构造函数 mySmartPtr<int> hpd = *hpa; // 拷贝构造函数
同时,无论复制shared_ptr
多少次,只要不出现循环引用,总是可以析构掉的,如
vector<mySmartPtr<Base> > obj; vector<mySmartPtr<Base> > obj2; obj2.push_back(obj[0]);
除了析构对象的工作,shared_ptr
同时也需要帮助我们解决类型转换的问题。一个是多态性的体现,即以基类指针指向派生类对象:
vector<mySmartPtr<Base> > obj; // 父类指针vectorobj.push_back(new Derived1); // 指向子类1obj.push_back(new Derived2); // 指向子类2
另一个是dynamic_cast
转型,如下,使用dynamic_cast<Derived2*>(static_cast<Base*>(new Derived1))
会得到空指针:
mySmartPtr<Derived1> d1 = new Derived1;mySmartPtr<Base> b = d1; // b指向的是Derived1mySmartPtr<Derived2> d2 = b.Cast<Derived2>(); // 转型失败返回空指针
具体来说,一个智能指针的实现要完成以下功能:
没有参数构造的时候,初始化为空,即对象和引用计数的两个指针都为0
使用指针为参数构造时,拥有此指针,在没有智能指针指向它时进行析构
智能指针复制时,两个智能指针共同拥有内部指针,引用计数同时+1
智能指针可以使用智能指针或普通指针重新赋值。重载=操作符,对于智能指针赋值,需要考虑是否自赋值,以避免将自身析构了后再重新赋值,而普通指针赋值给智能指针,则不需要考虑自赋值,因为两者本身是两个类型
获得底层指针的访问,定义
getPtrPointer()
和getPtrCounter()
来分别返回底层指针和引用计数,定义operator bool()
来处理智能指针隐式转换为bool
的情况重载
->
和×
操作符 ,来实现与普通指针相同的指针访问需要支持隐式指针类型转换,
static_cast
不支持而dynamic_cast
支持的转换则使用Cast<T2>()
成员函数来解决。考虑定义友元类,以防止指向派生类的智能指针有权限访问基类的内部对象;当转型不成功时,返回为空如果一个裸指针直接用来创建两个智能指针的话,期望的情况是当两个智能指针析构掉的时候,该指针会被delete两次从而崩溃(这是
shared_ptr
的特点)不处理循环引用(也是
shared_ptr
的特点),可以通过与weak_ptr
协作来打破循环暂时不实现
deleter
机制,即只能传递给mySmartPtr
一个参数
实际上,第8/第9两点往往是使用智能指针出现问题最多的地方,平时使用过程中要多加留意。
一个简单的实现
测试用例
/********************************************** > File Name: testmySmartPtr.cpp > Author: ye_create > Mail: > Created Time: 2015年05月28日 星期四 13时03分42秒 ***********************************************/#include <iostream>#include <vector>#include "mySmartPtr.h"using namespace std;class Base // 定义一个基类{public: virtual ~Base(){ cout << "class Base" << endl; }};class Derived1 : public Base // 派生类1{public: ~Derived1(){ cout << "class Derived1" << endl; }}; class Derived2 : public Base // 派生类2{public: ~Derived2(){ cout << "class Derived2" << endl; }};int main(){ int *pi = new int(42); mySmartPtr<int> *hpa(new mySmartPtr<int>(pi)); // 构造函数 mySmartPtr<int> *hpb = new mySmartPtr<int>(*hpa); // 拷贝构造函数 mySmartPtr<int> hpd = *hpa; // 拷贝构造函数 // 观察引用计数的变化 cout << hpa->getPtrCounter() << " " << hpb->getPtrCounter() << " "<< hpd.getPtrCounter() << endl; delete hpa; cout << hpd.getPtrCounter() << endl; delete hpb; cout << hpd.getPtrCounter() << endl; // 观察派生类向基类的隐式转换 vector<mySmartPtr<Base> > obj; // 父类指针vector obj.push_back(new Derived1); // 存入子类 obj.push_back(new Derived2); vector<mySmartPtr<Base> > obj2; obj2.push_back(obj[0]); if (obj2[0]) cout << "Cast Derived1 to Base successd" << endl; else cout << "Cast Derived1 to Base failed" << endl; // 观察不同类型的显式转换 mySmartPtr<Derived1> d1 = new Derived1; mySmartPtr<Base> b = d1; mySmartPtr<Derived2> d2 = b.Cast<Derived2>(); // d2是空,因为b指向的是Derived1而不是Derived2 if (d2) cout << "Cast Derived1 to Derived2 successd" << endl; else cout << "Cast Derived1 to Derived2 failed" << endl; return 0;}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
mySmartPtr实现
/************************************************************************* > File Name: mySmartPtr.cpp > Author: ye_create > Mail: > Created Time: 2015年05月28日 星期四 13时35分00秒 ************************************************************************/template <typename T>class mySmartPtr{public: // 构造函数 默认为空 mySmartPtr(): pointer(0), counter(0) { } // 形参为指针的构造函数 mySmartPtr(T* p): pointer(0), counter(0){ *this = p; } // 复制构造函数 mySmartPtr(const mySmartPtr<T> &p): pointer(p.pointer), counter(p.counter){ Increase(); } ~mySmartPtr(){ Decrease(); } // 指针的赋值操作符,类型不同,不是自赋值 mySmartPtr operator=(T* p){ Decrease(); if (p){ pointer = p; counter = new int(1); } else { pointer = 0; counter = 0; } return *this; } // 智能指针赋值操作符 mySmartPtr operator=(mySmartPtr<T> &p){ // 处理自赋值 if (this != &p){ Decrease(); pointer = p.pointer; counter = p.counter; Increase(); } return *this; } operator bool() const{ return counter != 0; } // ×操作符重载 T* operator*() const{ return this; } // ->操作符重载 T* operator->() const{ return pointer; } // 返回底层指针 int getPtrPointer() const{ return *pointer; } // 返回引用计数 int getPtrCounter() const{ return *counter; } // 处理父类子类的情况, ptr<derived>不能访问 ptr<based>的内部对象 template<typename C> friend class mySmartPtr; template<typename C> mySmartPtr(const mySmartPtr<C> &p): pointer(p.pointer), counter(p.counter){ Increase(); } template<typename C> mySmartPtr<T> & operator=(const mySmartPtr<C> &p){ Decrease(); pointer = p.pointer; counter = p.counter; Increase(); return *this; } // 处理内部使用 dynamic_cast做判断的转换,失败则空指针 template<typename C> mySmartPtr<C> Cast() const{ C* converted = dynamic_cast<C*>(pointer); mySmartPtr<C> result; if (converted){ result.pointer = converted; result.counter = counter; result.Increase(); } return result; }private: T* pointer; int* counter; void Increase(){ if (counter) ++*counter; } void Decrease(){ if (counter && --*counter == 0){ delete pointer; delete counter; counter = 0; pointer = 0; } }};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- shared_ptr原理分析及实现
- 智能指针原理分析与自己的shared_ptr实现
- boost::shared_ptr 分析与实现
- boost::shared_ptr 分析与实现
- boost::shared_ptr 分析与实现
- boost::shared_ptr 分析与实现
- boost::shared_ptr 分析与实现
- boost::shared_ptr 分析与实现
- boost 智能指针 shared_ptr 原理分析
- 智能指针(二):shared_ptr实现原理
- 智能指针(二):shared_ptr实现原理
- 智能指针(二):shared_ptr实现原理
- 模板共享指针(shared_ptr)原理实现
- C++ 智能指针shared_ptr模板实现原理
- 智能指针(二):shared_ptr实现原理
- ARM920T虚拟地址原理分析及实现
- B树的原理分析及实现
- 放大镜原理分析及jquery实现
- nc 命令 聊天
- 机器人医生坐诊:只需45秒_检测你是否“抑郁”或“痴呆”
- map常见用法
- oracle数据库的简单增删改查
- 无ldf日志文件附加数据库
- shared_ptr原理分析及实现
- openssl 1.1.0f 生成中文证书乱码的解决方案
- python编程实例-循环嵌套-九九乘法表
- CDESK激活附加功能T-CODE: CDESK_CUS
- Android集成百度地图(第一节)
- HTML1.1.z
- Server【SVN环境搭建】关于toroiseSVN Checkout 卡死现象
- [My SQL] 使用存储过程
- 中序表达式转后序的代码