智能指针:模拟实现auto_ptr,scoped_ptr,shared_ptr
2016-06-11 18:52
246 查看
RAII(Resource Acquisition Is Initialization)
资源分配即初始化,定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放。
所谓智能指针就是智能/自动化的管理指针所指向的动态资源的释放。
STL--auto_ptr
Boost库的智能指针:scoped_ptr/scoped_array/shared_ptr/shared_array/weak_ptr/intrusive_ptr.
(ps:新的C++11标准中智能指针:模拟实现auto_ptr,scoped_ptr,shared_ptr已经引入了unique_ptr/shared_ptr/weak_ptr)
常见的智能指针有:auto_ptr/scoped_ptr/scoped_array/shared_ptr/shared_array。由于scoped_array和scoped_ptr比较类似,shared_array和shared_ptr又比较类似,所以我们只实现auto_ptr/scoped_ptr/shared_ptr。
下面介绍三种智能指针auto_ptr,scoped_ptr,shared_ptr。
智能指针的基本特点:
1)智能指针管理的是一块内存的释放。
2)智能指针是一个类,有类似指针的功能。
1、AutoPtr
首先auto_ptr的成员变量主要有T* _ptr,bool _owner时,主要实现原理是在构造对象时赋予其管理空间的所有权,在析构函数中通过_owner的真否来释放所有权,并且在拷贝或赋值后通过将_owner设为false,转移空间的所有权。但是此做法存在一些问题,主要问题是如果拷贝出来的对象比原来的对象出作用域,则原来的对象的_owner虽然为false,但会访问一块已经释放的一块空间。
改进后还是管理空间的所有权转移,但这种实现方法中没有_owner。构造和析构和上述实现方法类似,但拷贝和赋值后直接将_ptr赋为空,禁止其在访问原来的内存空间。
因为智能指针容易出现拷贝时释放两次的情况,所以ScopedPtr主要是进行防止拷贝,防止拷贝的两条必须要满足的条件是:
1)设置保护限定符;2)对拷贝构造函数和赋值运算符重载进行之声明不定义。
如没有设置保护限定符,若在类外进行定义后,则会出现问题,所以说这两个条件是必不可少的。这样就能够避免上面所出现的问题,但是这样就造成了它在功能上的缺陷。
【小知识】模板的分离编译
1、在模板头文件 xxx.h 里面显示实例化->模板类的定义后面添加 template class SeqList<int >; 一般不推荐这种方法,一方面老编译器可能不支持,另一方面实例化依赖调用者。(不推荐)
2、将声明和定义放到一个文件 "xxx.hpp" 里面,推荐使用这种方法。
以上是本人对智能指针的一些认识,如有不足,请多指教。
本文出自 “Scen” 博客,请务必保留此出处http://10741357.blog.51cto.com/10731357/1753272
资源分配即初始化,定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放。
所谓智能指针就是智能/自动化的管理指针所指向的动态资源的释放。
STL--auto_ptr
Boost库的智能指针:scoped_ptr/scoped_array/shared_ptr/shared_array/weak_ptr/intrusive_ptr.
(ps:新的C++11标准中智能指针:模拟实现auto_ptr,scoped_ptr,shared_ptr已经引入了unique_ptr/shared_ptr/weak_ptr)
常见的智能指针有:auto_ptr/scoped_ptr/scoped_array/shared_ptr/shared_array。由于scoped_array和scoped_ptr比较类似,shared_array和shared_ptr又比较类似,所以我们只实现auto_ptr/scoped_ptr/shared_ptr。
下面介绍三种智能指针auto_ptr,scoped_ptr,shared_ptr。
智能指针的基本特点:
1)智能指针管理的是一块内存的释放。
2)智能指针是一个类,有类似指针的功能。
1、AutoPtr
首先auto_ptr的成员变量主要有T* _ptr,bool _owner时,主要实现原理是在构造对象时赋予其管理空间的所有权,在析构函数中通过_owner的真否来释放所有权,并且在拷贝或赋值后通过将_owner设为false,转移空间的所有权。但是此做法存在一些问题,主要问题是如果拷贝出来的对象比原来的对象出作用域,则原来的对象的_owner虽然为false,但会访问一块已经释放的一块空间。
改进后还是管理空间的所有权转移,但这种实现方法中没有_owner。构造和析构和上述实现方法类似,但拷贝和赋值后直接将_ptr赋为空,禁止其在访问原来的内存空间。
//智能指针AutoPtr template<class T> class AutoPtr { public: AutoPtr(T* ptr) :_ptr(ptr) {} AutoPtr(AutoPtr<T> &ap) :_ptr(ap._ptr) {//拷贝后将原有指向同一块内存的指针置空 ap._ptr = NULL; } AutoPtr<T>& operator=(AutoPtr<T> &ap) { if (this != &ap) { delete _ptr; _ptr = ap._ptr; ap._ptr = NULL; } return *this; } T& operator*() { return *_ptr; } T* GetPtr()//返回原指针_ptr { return _ptr; } T* operator->() { return _ptr; } ~AutoPtr() { cout << "~AutoPtr()" << endl; if (_ptr) { delete _ptr; _ptr = NULL; } } private: T* _ptr; };2、ScopedPtr
因为智能指针容易出现拷贝时释放两次的情况,所以ScopedPtr主要是进行防止拷贝,防止拷贝的两条必须要满足的条件是:
1)设置保护限定符;2)对拷贝构造函数和赋值运算符重载进行之声明不定义。
如没有设置保护限定符,若在类外进行定义后,则会出现问题,所以说这两个条件是必不可少的。这样就能够避免上面所出现的问题,但是这样就造成了它在功能上的缺陷。
3、SharedPtr SharedPtr指针主要的原理是利用引用计数的浅拷贝来实现,通过多开辟4个字节的方式,存储引用计数,当有指针指向这块空间时,引用计数+1。如若析构时,先将这块空间的引用计数降为1,然后在进行析构,避免了析构多次的问题。然而上面用引用计数实现的简化版看起来不错,但却存在一些问题,具体关注本人博客。//智能指针SharedPtr template<class T> class SharedPtr { public: SharedPtr() :_ptr(NULL) , _pCount(new long(1)) {} SharedPtr(T* ptr) :_ptr(ptr) , _pCount(new long(1)) {} //----引用计数浅拷贝 SharedPtr(const SharedPtr<T> &sp) :_ptr(sp._ptr) , _pCount(sp._pCount) { ++(*_pCount); } SharedPtr<T>& operator=(const SharedPtr<T> &sp) { if (this != &sp) { Release(); _ptr = sp._ptr; _pCount = sp._pCount; ++(*_pCount); } return *this; } ////用深拷贝 ////传统写法 //SharedPtr<T>& operator=(const SharedPtr<T>& sp) //{ // if (this != &sp) // { // SharedPtr<T> tmp(sp); // swap(_ptr, tmp._ptr); // swap(_pCount, tmp._pCount); // } // return *this; //} //现代写法 //SharedPtr<T>& operator=(SharedPtr<T> sp) //{ // swap(_ptr, sp._ptr); // swap(_pCount, sp._pCount); // return *this; //} ~SharedPtr() { cout << "~SharedPtr()" << endl; Release(); } T& operator*() { return *_ptr; } T* GetPtr()//返回原指针_ptr { return _ptr; } T* operator->() { return _ptr; } long GetCount()//返回同一内存的指向数 { return *_pCount; } void Release()//释放内存 { if (--(*_pCount) == 0) { delete _ptr; delete _pCount; _ptr = NULL; _pCount = NULL; } } private: T* _ptr; long* _pCount; };测试用例如下:
void Test()
{
AutoPtr<int> ap1(new int(3));
AutoPtr<int> ap2 = ap1;
AutoPtr<int> ap3(new int(5));
ap3 = ap2;
ScopedPtr<int> sp1(new int(3));
//防拷贝
//ScopedPtr<int> sp2 = sp1;
//sp2 = sp1;
SharedPtr<int> shp1(new int(3));
SharedPtr<int> shp2 = shp1;
SharedPtr<int> shp3(new int(5));
shp3 = shp1;
*shp1 = 6;
cout << &shp1 << endl;
cout << shp1.GetCount() << endl;
cout << *(shp1.GetPtr()) << endl;//输出_ptr的值
cout << *(shp1.operator->()) << endl;//输出_ptr的值
SharedPtr<Node> shpNode(new Node);
shpNode->_data = 10;
cout << shpNode->_data << endl;
}
struct Node
{
int _data;
Node* _next;
};
【小知识】模板的分离编译
1、在模板头文件 xxx.h 里面显示实例化->模板类的定义后面添加 template class SeqList<int >; 一般不推荐这种方法,一方面老编译器可能不支持,另一方面实例化依赖调用者。(不推荐)
2、将声明和定义放到一个文件 "xxx.hpp" 里面,推荐使用这种方法。
以上是本人对智能指针的一些认识,如有不足,请多指教。
本文出自 “Scen” 博客,请务必保留此出处http://10741357.blog.51cto.com/10731357/1753272
相关文章推荐
- CentOs6.5 安装mysql
- Hadoop2.6.4伪分布式安装
- 耐火砖尺寸思考
- Cloudera Hadoop架构及Hadoop Common实现原理
- top命令的解释
- Linux下python升级至2.7步骤
- mfs分布式存储+master端高可用
- Docker集群管理工具-Kubernetes部署记录
- linux-磁盘文件系统管理
- CentOS 6.5下编译FreeSWITCH1.6.6
- linux下端口的监听
- 嵌入式Linux裸机开发(十五)——LCD
- linux中curl命令
- 做10年Windows程序员与做10年Linux程序员的区别
- WIN-CE系统架构
- GCC、ARM-LINUX-GCC、ARM-ELF-GCC浅析
- 基于Redux架构的单页应用开发总结(三)
- task_struct结构体字段介绍--Linux中的PCB
- TopDom找TAD
- linux c 获取系统当前时区