读书笔记_Effective_C++_条款十五:在资源类管理类中提供对原始资源的访问
先放上自己写的MySharedPtr类,这是仿照shared_ptr的功能来实现的(实际shared_ptr要复杂的多)。
1 #ifndef MY_SHARED_PTR_H 2 #define MY_SHARED_PTR_H 3 4 #include <iostream> 5 using namespace std; 6 7 8 typedef void (*FP)(); 9 10 template <class T> 11 class MySharedPtr 12 { 13 14 private: 15 T *ptr; 16 size_t *count; 17 FP Del; // 声明一个删除器 18 static void swap(MySharedPtr& obj1, MySharedPtr& obj2) 19 { 20 std::swap(obj1.ptr, obj2.ptr); 21 std::swap(obj1.count, obj2.count); 22 std::swap(obj1.Del, obj2.Del); 23 } 24 25 public: 26 MySharedPtr(T* p = NULL): ptr(p), count(new size_t(1)),Del(NULL){} 27 28 // 添加带删除器的构造函数 29 MySharedPtr(T* p, FP fun): ptr(p), count(new size_t(1)), Del(fun){} 30 31 32 MySharedPtr(MySharedPtr& p): ptr(p.ptr), count(p.count), Del(p.Del) 33 { 34 ++ *p.count; 35 } 36 37 38 39 MySharedPtr& operator= (MySharedPtr& p) 40 { 41 if(this != &p && (*this).ptr != p.ptr) 42 { 43 MySharedPtr temp(p); 44 swap(*this, temp); 45 } 46 return *this; 47 } 48 49 ~MySharedPtr() 50 { 51 if(Del != NULL) 52 { 53 Del(); 54 } 55 reset(); 56 } 57 58 void reset() 59 { 60 -- *count; 61 if(*count == 0) 62 { 63 delete ptr; 64 ptr = 0; 65 delete count; 66 count = 0; 67 } 68 } 69 70 bool unique() const 71 { 72 return *count == 1; 73 } 74 75 size_t use_count() const 76 { 77 return *count; 78 } 79 80 }; 81 82 #endif /* MY_SHARED_PTR_H */
这个MySharedPtr已经可以进行资源管理了,当资源的生命周期结束后,可以自动将之删除。但对于使用者来说,还是会遇到一些问题,比如:
1 void PrintIntPointerValue(int* p) 2 { 3 cout << *p << endl; 4 } 5 6 int main() 7 { 8 MySharedPtr<int> ptr(new int(2)); 9 PrintIntPointerValue(ptr); 10 }
因为PrintIntPointerValue函数需要原始指针,但MySharedPtr类中并没有提供这一个接口,所以如果像上面程序一样强行使用,就会报编译错,如下所示:
解决的方法其实挺简单的,就是在类中添加一个名为get()的函数,这个函数返回原始指针。也许会有人质疑这种做法,因为这样原始指针的阴影又会浮现出来,它提供了一个让用户可能出错的接口。
但书上有句话说的好:一个好的class应是“隐藏了客户不需要看的部分,但备妥客户需要的所有东西”。
除了get()外,也可以使用隐式转换函数:
1 operator T*() const 2 { 3 return ptr; 4 }
但这种方法往往会使程序陷入漏洞之中,比如:
MySharedPtr<int> sharedPtr(new int(3));
int *ordinaryPtr = sharedPtr; // 这样编译会通过,但一旦sharedPtr释放资源后,ordinaryPtr便会成为悬空指针(ordinaryPtr并会使引用计数加一,也不会参与到资源释放的操作中来)
所以一般而言,提供一个get()函数是比较好的选择。另外,为了方便访问,还应该重载*和->操作符,使得MySharedPtr对象的操作与原始指针的操作一样方便,如下所示:
1 T& operator* () const 2 { 3 return *ptr; 4 } 5 6 T* operator-> () const 7 { 8 return ptr; 9 } 10 11 T* get() const 12 { 13 return ptr; 14 }
最后总结一下(MysharedPtr的完整版本可以参照条款十四的读书笔记):
- 一些应用程序接口往往要求访问到原始资源,所以每一个RAII类都应该提供一个“取得所管理之资源”的方法
- 对原始资源的访问可能经由显式或隐式转换。一般而言显式转换比较安全(用get而不用operator T*())