C++侵入式智能指针的实现
简介
在现代c++中,智能指针给我们在资源管理上带来了很多好处,这里就不多说了。
在工作中,我们常常会用智能指针来管理资源,其中最常用的就是引用计数类智能指针了(shared_ptr)。
资源共享型的智能指针有两种实现,一种是侵入式,一种是非侵入式。
在教材里比较常见的是非侵入式的,它的实现完全放在智能指针模板里,模板类有一个用于保存资源类对象的指针变量,和一个用于记录资源对象使用计数的指针变量,这两个东西是所有的智能指针对象共享的,所以通过指针保存。
而侵入式则不同,它的实现分散在智能指针模板和使用智能指针模板的类中:模板类只有一个用于保存对象的指针变量,对象的计数放在了资源类中。
vc+436osyrntw8tatobssrhivc/j2aos0rkxyl3pslliq6o7pc9wpg0kpha+mqgi0vloqtl908o8xsr9toa0ottatttp87g+ye2jrmv50ttu2rqvyv2199pdtctksbryv8ns1naxvdo0q7xd18rutlbuz/o12na3o6y2+lk708o1o9de0v3tw7zgyv3wtbaqyqejqlfhx9bi68q91sfe3na41eu21m/ztcs/vbg0o6yx2ndrtpjxxdbhxnzwunxrxkow5aost/hu8r7nu+gz9s/wtttp89l908o8xsr9tqrkp6opoam8l3a+dqo8cd7isbxjysejujwvcd4ncjxwpjghotfk1lta4lhy0ovt0nl908o8xsr9setbv6ossqlh0rjdsetbv7xe1pa89b/j0tsxu8fwyovkvdbhxnzwunxrxkow5bv5wocy2df3o6zv4s/utcpc6bezo7s8l3a+dqo8cd4yoali57n7umpa4lkisrvp68q508pwx8tc1rjv66osy/y7ucrhu+g0+nff0v3tw7zgyv2x5mg/oam8l3a+dqo8cd7b7c3io6zwx8tc1rjv69pq0ru49s7et6ix3mpitctoyszio6y+zcrh0a27t9l908ohozwvcd4ncjxomibpzd0="侵入式智能指针实现">侵入式智能指针实现
两个要点:
1.将引用计数变量从资源类中抽离出来,封装成一个基类,该基类包含了引用计数变量。如果一个类想使用智能指针,则只需要继承自该基类即可;
2.引用计数的基类,设计成模板类,接受引用计数类型作为参数,比如使用int类型或者原子计数类型作为引用计数变量。默认情况下应该使用原子计数类型作为引用计数变量。
3.引用计数基类的构造函数、拷贝构造函数、析构函数应为protected,即该类只能通过继承关系被使用。
4.拷贝构造函数并不拷贝引用计数基类的数据成员,而是重新将原子计数_atomic置为0——因为每个对象都有一个自己的引用计数,当发生对象拷贝构造时,新的对象的计数应该置为0,而不应该拷贝旧对象的计数。
5.赋值操作operator=,比如a=b,同上面一样,只对资源类的成员进行拷贝,而不拷贝其引用计数基类的数据成员。也就是说,将b的值赋给a,a的引用计数应该保持不变,而不能将b的引用计数拷贝过来——这是对象的拷贝,而不是智能指针的拷贝。
首先实现引用计数基类(注:atomic的实现这里就不给出了):
/** * @brief 智能指针基类. * * 所有需要智能指针支持的类都需要从该对象继承, * * 内部采用引用计数atomic实现,对象可以放在容器中; */ template class handlebaset { public: /** 原子计数类型*/ typedef t atomic_type; /** * @brief 复制。引用计数不能被复制。 * * @return handlebase& */ handlebaset& operator=(const handlebaset&) { return *this; } /** * @brief 增加计数 */ void incref() { _atomic.inc_fast(); } /** * @brief 减少计数, 当计数==0时, 且需要删除数据时, 释放对象 */ void decref() { if(_atomic.dec_and_test() && !_bnodelete) { _bnodelete = true; delete this; } } /** * @brief 获取计数. * * @return int 计数值 */ int getref() const { return _atomic.get(); } /** * @brief 设置不自动释放. * * @param b 是否自动删除,true or false */ void setnodelete(bool b) { _bnodelete = b; } protected: /** * @brief 构造函数 */ handlebaset() : _atomic(0), _bnodelete(false) { } /** * @brief 拷贝构造,_atomic和_bnodelete不能被拷贝,只能重置 */ handlebaset(const handlebaset&) : _atomic(0), _bnodelete(false) { } /** * @brief 析构 */ virtual ~handlebaset() { } protected: /** * 计数 */ atomic_type _atomic; /** * 是否自动删除 */ bool _bnodelete; }; // 针对int类型计数变量的特化 // 在类声明中定义的函数将自动inline,类外定义的函数需显式inline template<> inline void handlebaset::incref() { ++_atomic; } template<> inline void handlebaset::decref() { if(--_atomic == 0 && !_bnodelete) { _bnodelete = true; delete this; } } template<> inline int handlebaset::getref() const { return _atomic; } // 默认使用atomic作为引用计数类型 typedef handlebaset handlebase;
以上实现了计数基类,所有需要使用智能指针的对象必须继承自该类。
智能指针模板类的实现需要关注的几个点:
1.初始化、赋值等操作需要考虑参数是原始指针、其他类型的智能指针初、用同一类型的智能指针三种情况。
2.需要重载<、=、!=几个操作(非成员函数),左右操作数使用两个模板参数。
3.赋值操作需要检查自我赋值。
/** * @brief 空指针异常 */ struct shared_ptrnull_exception : public exception { shared_ptrnull_exception(const string &buffer) : exception(buffer){}; ~shared_ptrnull_exception() throw(){}; }; /** * @brief 智能指针模板类. * * 可以放在容器中,且线程安全的智能指针. * * 通过它定义智能指针,该智能指针通过引用计数实现, * * 可以放在容器中传递. * * template t必须继承于handlebase */ template class shared_ptr { public: /** * 元素类型 */ typedef t element_type; /** * @brief 用原生指针初始化, 计数+1. * * @param p */ shared_ptr(t* p = 0) { _ptr = p; if(_ptr) { _ptr->incref(); } } /** * @brief 用其他智能指针r的原生指针初始化, 计数+1. * * @param y * @param r */ template shared_ptr(const shared_ptr& r) { _ptr = r._ptr; if(_ptr) { _ptr->incref(); } } /** * @brief 拷贝构造, 计数+1. * * @param r */ shared_ptr(const shared_ptr& r) { _ptr = r._ptr; if(_ptr) { _ptr->incref(); } } /** * @brief 析构 */ ~shared_ptr() { if(_ptr) { _ptr->decref(); } } /** * @brief 赋值, 普通指针. * * @param p * @return shared_ptr& */ shared_ptr& operator=(t* p) { if(_ptr != p) { if(p) { p->incref(); } t* ptr = _ptr; _ptr = p; if(ptr) { ptr->decref(); } } return *this; } /** * @brief 赋值, 其他类型智能指针. * * @param y * @param r * @return shared_ptr& */ template shared_ptr& operator=(const shared_ptr& r) { if(_ptr != r._ptr) { if(r._ptr) { r._ptr->incref(); } t* ptr = _ptr; _ptr = r._ptr; if(ptr) { ptr->decref(); } } return *this; } /** * @brief 赋值, 该类型其他执政指针. * * @param r * @return shared_ptr& */ shared_ptr& operator=(const shared_ptr& r) { if(_ptr != r._ptr) { if(r._ptr) { r._ptr->incref(); } t* ptr = _ptr; _ptr = r._ptr; if(ptr) { ptr->decref(); } } return *this; } /** * @brief 将其他类型的智能指针换成当前类型的智能指针. * * @param y * @param r * @return shared_ptr */ template static shared_ptr dynamiccast(const shared_ptr& r) { return shared_ptr(dynamic_cast(r._ptr)); } /** * @brief 将其他原生类型的指针转换成当前类型的智能指针. * * @param y * @param p * @return shared_ptr */ template static shared_ptr dynamiccast(y* p) { return shared_ptr(dynamic_cast(p)); } /** * @brief 获取原生指针. * * @return t* */ t* get() const { return _ptr; } /** * @brief 调用. * * @return t* */ t* operator->() const { if(!_ptr) { thrownullhandleexception(); } return _ptr; } /** * @brief 引用. * * @return t& */ t& operator*() const { if(!_ptr) { thrownullhandleexception(); } return *_ptr; } /** * @brief 是否有效. * * @return bool */ operator bool() const { return _ptr ? true : false; } /** * @brief 交换指针. * * @param other */ void swap(shared_ptr& other) { std::swap(_ptr, other._ptr); } protected: /** * @brief 抛出异常 */ void thrownullhandleexception() const; public: t* _ptr; }; /** * @brief 抛出异常. * * @param t * @param file * @param line */ template inline void shared_ptr::thrownullhandleexception() const { throw shared_ptrnull_exception("shared_ptr null handle error"); } /** * @brief ==判断. * * @param t * @param u * @param lhs * @param rhs * * @return bool */ template inline bool operator==(const shared_ptr& lhs, const shared_ptr& rhs) { t* l = lhs.get(); u* r = rhs.get(); if(l && r) { return *l == *r; } else { return !l && !r; } } /** * @brief 不等于判断. * * @param t * @param u * @param lhs * @param rhs * * @return bool */ template inline bool operator!=(const shared_ptr& lhs, const shared_ptr& rhs) { t* l = lhs.get(); u* r = rhs.get(); if(l && r) { return *l != *r; } else { return l || r; } } /** * @brief 小于判断, 用于放在map等容器中. * * @param t * @param u * @param lhs * @param rhs * * @return bool */ template inline bool operator<(const shared_ptr& lhs, const shared_ptr& rhs) { t* l = lhs.get(); u* r = rhs.get(); if(l && r) { return *l < *r; } else { return !l && r; } }
使用示例
class test: public handlebase { // ... }; shared_ptr mytestclass = new test();
推荐阅读
-
C++智能指针,指针容器原理及简单实现(auto_ptr,scoped_ptr,ptr_vector).
-
浅谈C++普通指针和智能指针管理动态内存的陷阱
-
C++侵入式智能指针的实现
-
c++中的智能指针
-
C++中的智能指针实现
-
采用可变式分区管理,使用首次适应算法实现主存的分配与回收(C++实现)
-
采用可变式分区管理,使用空闲区链实现主存的分配与回收(C++实现)
-
C++使用智能指针实现模板形式的单例类
-
【SSH进阶之路】一步步重构容器实现Spring框架——解决容器对组件的“侵入式”管理的两种方案--主动查找和控制反转(九)
-
【SSH进阶之路】一步步重构容器实现Spring框架——解决容器对组件的“侵入式”管理的两种方案--主动查找和控制反转(九)