读书笔记_Effective_C++_条款四十五:运用成员函数模板接受所有兼容类型
比如有一个Base类和一个Derived类,像下面这样:
1 class BaseClass 2 {…}; 3 4 class DerivedClass : public BaseClass 5 {…};
因为是父类与子类的关系,所以可以这样写:
1 DerivedClass *d; 2 BaseClass *b = static_cast< BaseClass *>d; // 用C风格直接是 b = (BaseClass*) d;
我们可以弄一个简易的Shared型智能指针类,如果直接像下面这样写:
1 template <class T> 2 class SharedPtr 3 { 4 private: 5 T* Ptr; 6 static size_t Count; 7 8 public: 9 SharedPtr(T* _ptr) 10 { 11 Count = 1; 12 Ptr = _ptr; 13 cout << "Constructor Called Same" << endl; 14 } 15 ~SharedPtr() 16 { 17 cout << "Destructor Called" << endl; 18 if (--Count == 0) 19 { 20 cout << "Delete Pointer" << endl; 21 delete Ptr; 22 Ptr = NULL; 23 } 24 } 25 26 SharedPtr(const SharedPtr<T>& _Smart) 27 { 28 cout << "Copy Constructor Called Same" << endl; 29 Ptr = _Smart.Ptr; 30 ++Count; 31 } 32 };
那么显示编译器不会允许SharedPtr<BaseClass> pb(pd),因为在编译期替换T时,拷贝构造函数明确了接受类型必须是SharedPtr<BaseClass>,而由SharedPtr<DerivedClass>对象至SharedPtr<BaseClass>的转换并不存在,所以编译器报错。
为了能使这样的转换合法,我们需要在原来的基础上这样写:
1 class SharedPtr 2 { 3 private: 4 T* Ptr; 5 static size_t Count; 6 7 public: 8 SharedPtr(T* _ptr) 9 { 10 Count = 1; 11 Ptr = _ptr; 12 cout << "Constructor Called Same" << endl; 13 } 14 15 template <class Other> 16 SharedPtr(Other* _ptr) 17 { 18 Count = 1; 19 Ptr = static_cast<T*> (_ptr); 20 cout << "Constructor Called Other" << endl; 21 } 22 23 ~SharedPtr() 24 { 25 cout << "Destructor Called" << endl; 26 if (--Count == 0) 27 { 28 cout << "Delete Pointer" << endl; 29 delete Ptr; 30 Ptr = NULL; 31 } 32 } 33 34 T* GetPointer() 35 { 36 return Ptr; 37 } 38 39 T* GetPointer() const 40 { 41 return Ptr; 42 } 43 44 SharedPtr(const SharedPtr<T>& _Smart) 45 { 46 cout << "Copy Constructor Called Same" << endl; 47 Ptr = _Smart.Ptr; 48 ++Count; 49 } 50 51 52 template <class Other> 53 SharedPtr(const SharedPtr<Other>& _Smart) 54 { 55 cout << "Copy Constructor Called Other" << endl; 56 Ptr = static_cast<T*>(_Smart.GetPointer()); 57 ++Count; 58 } 59 };
注意代码标注为蓝色的部分(即为泛化部分),这里另外声明了一个模板参数Other,它可以与T相同,也可以不同,也就意味着它可以接受任何可以转化成T的类型了,比如父子类。这里还定义了GetPointer的方法,因为拷贝构建中传入的对象不一定是属于同一个类的,所以不能保证可以访问到类的私有成员。Ptr = static_cast<T*>(_Smart.GetPointer())这句话其实就是转换的实质了,只要任何可以转成T*的Other*,都是可以通过编译的,但如果是风马牛不相及的两个类,就不会通过编译。这里有一点要强调一下,我们可以把double转成int(窄化),也可以把int转成double(宽化),但注意double*与int*之间是不能相互转的,如果这样写int *a = (int*) (new double(2)),是不能通过编译的,可以用static_cast转换的类要有继承关系。代码还有赋值运算符也需要提供一个非泛化的版本和泛化的版本,这里简略没有写出。
这里还有一个需要注意的地方,在class类声明泛化copy构造函数(member template),并不会阻止编译器生成它们自己的copy构造函数(non-template),换言之,如果程序中只写了泛化的copy构造函数,那么编译器还是会自动生成一个非泛化的版本出来,如果不想要这个缺省版本,那一定不能偷懒,要两个版本的copy构造函数都要写。
最后总结一下:
1. 请使用member function templates(成员函数模板)生成“可接受所有兼容类型”的函数;
2. 如果你声明member templates用于“泛化copy构造”或“泛化assignment操作”,你还是需要声明正常的copy构造函数和copy assignment操作符。
下面附上微软对shared_ptr类的声明,对比黄色加亮部分,可以看到本条款所说的技术要点。
1 template<class Ty> 2 class shared_ptr { 3 public: 4 typedef Ty element_type; 5 6 shared_ptr(); 7 shared_ptr(nullptr_t); 8 shared_ptr(const shared_ptr& sp); 9 shared_ptr(shared_ptr&& sp); 10 template<class Other> 11 explicit shared_ptr(Other * ptr); 12 template<class Other, class D> 13 shared_ptr(Other * ptr, D dtor); 14 template<class D> 15 shared_ptr(nullptr_t, D dtor); 16 template<class Other, class D, class A> 17 shared_ptr(Other *ptr, D dtor, A alloc); 18 template<class D, class A> 19 shared_ptr(nullptr_t, D dtor, A alloc); 20 template<class Other> 21 shared_ptr(const shared_ptr<Other>& sp); 22 template<class Other> 23 shared_ptr(const shared_ptr<Other>&& sp); 24 template<class Other> 25 explicit shared_ptr(const weak_ptr<Other>& wp); 26 template<class Other> 27 shared_ptr(auto_ptr<Other>& ap); 28 template<class Other, class D> 29 shared_ptr(unique_ptr<Other, D>&& up); 30 template<class Other> 31 shared_ptr(const shared_ptr<Other>& sp, Ty *ptr); 32 ~shared_ptr(); 33 shared_ptr& operator=(const shared_ptr& sp); 34 template<class Other> 35 shared_ptr& operator=(const shared_ptr<Other>& sp); 36 shared_ptr& operator=(shared_ptr&& sp); 37 template<class Other> 38 shared_ptr& operator=(shared_ptr<Other>&& sp); 39 template<class Other> 40 shared_ptr& operator=(auto_ptr< Other >&& ap); 41 template <class Other, class D> 42 shared_ptr& operator=(const unique_ptr< Other, D>& up) = delete; 43 template <class Other, class D> 44 shared_ptr& operator=(unique_ptr<Other, D>&& up); 45 void swap(shared_ptr& sp); 46 void reset(); 47 template<class Other> 48 void reset(Other *ptr); 49 template<class Other, class D> 50 void reset(Other *ptr, D dtor); 51 template<class Other, class D, class A> 52 void reset(Other *ptr, D dtor, A alloc); 53 Ty *get() const; 54 Ty& operator*() const; 55 Ty *operator->() const; 56 long use_count() const; 57 bool unique() const; 58 operator bool() const; 59 60 template<class Other> 61 bool owner_before(shared_ptr<Other> const& ptr) const; 62 template<class Other> 63 bool owner_before(weak_ptr<Other> const& ptr) const; 64 template<class D, class Ty> 65 D* get_deleter(shared_ptr<Ty> const& ptr); 66 };