比如有一个Base类和一个Derived类,像下面这样:

 class BaseClass
{…}; class DerivedClass : public BaseClass
{…};

因为是父类与子类的关系,所以可以这样写:

 DerivedClass *d;
BaseClass *b = static_cast< BaseClass *>d; // 用C风格直接是 b = (BaseClass*) d;

我们可以弄一个简易的Shared型智能指针类,如果直接像下面这样写:

 template <class T>
class SharedPtr
{
private:
T* Ptr;
static size_t Count; public:
SharedPtr(T* _ptr)
{
Count = ;
Ptr = _ptr;
cout << "Constructor Called Same" << endl;
}
~SharedPtr()
{
cout << "Destructor Called" << endl;
if (--Count == )
{
cout << "Delete Pointer" << endl;
delete Ptr;
Ptr = NULL;
}
} SharedPtr(const SharedPtr<T>& _Smart)
{
cout << "Copy Constructor Called Same" << endl;
Ptr = _Smart.Ptr;
++Count;
}
};

那么显示编译器不会允许SharedPtr<BaseClass> pb(pd),因为在编译期替换T时,拷贝构造函数明确了接受类型必须是SharedPtr<BaseClass>,而由SharedPtr<DerivedClass>对象至SharedPtr<BaseClass>的转换并不存在,所以编译器报错。

为了能使这样的转换合法,我们需要在原来的基础上这样写:

 class SharedPtr
{
private:
T* Ptr;
static size_t Count; public:
SharedPtr(T* _ptr)
{
Count = ;
Ptr = _ptr;
cout << "Constructor Called Same" << endl;
} 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 }

~SharedPtr()
{
cout << "Destructor Called" << endl;
if (--Count == )
{
cout << "Delete Pointer" << endl;
delete Ptr;
Ptr = NULL;
}
} T* GetPointer()
{
return Ptr;
} T* GetPointer() const
{
return Ptr;
} SharedPtr(const SharedPtr<T>& _Smart)
{
cout << "Copy Constructor Called Same" << endl;
Ptr = _Smart.Ptr;
++Count;
} 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 }
};

注意代码标注为蓝色的部分(即为泛化部分),这里另外声明了一个模板参数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类的声明,对比黄色加亮部分,可以看到本条款所说的技术要点。

 template<class Ty>
class shared_ptr {
public:
typedef Ty element_type; shared_ptr();
shared_ptr(nullptr_t);
shared_ptr(const shared_ptr& sp);
shared_ptr(shared_ptr&& sp);
template<class Other>
explicit shared_ptr(Other * ptr);
template<class Other, class D>
shared_ptr(Other * ptr, D dtor);
template<class D>
shared_ptr(nullptr_t, D dtor);
template<class Other, class D, class A>
shared_ptr(Other *ptr, D dtor, A alloc);
template<class D, class A>
shared_ptr(nullptr_t, D dtor, A alloc);
template<class Other>
shared_ptr(const shared_ptr<Other>& sp);
template<class Other>
shared_ptr(const shared_ptr<Other>&& sp);
template<class Other>
explicit shared_ptr(const weak_ptr<Other>& wp);
template<class Other>
shared_ptr(auto_ptr<Other>& ap);
template<class Other, class D>
shared_ptr(unique_ptr<Other, D>&& up);
template<class Other>
shared_ptr(const shared_ptr<Other>& sp, Ty *ptr);
~shared_ptr();
shared_ptr& operator=(const shared_ptr& sp);
template<class Other>
shared_ptr& operator=(const shared_ptr<Other>& sp);
shared_ptr& operator=(shared_ptr&& sp);
template<class Other>
shared_ptr& operator=(shared_ptr<Other>&& sp);
template<class Other>
shared_ptr& operator=(auto_ptr< Other >&& ap);
template <class Other, class D>
shared_ptr& operator=(const unique_ptr< Other, D>& up) = delete;
template <class Other, class D>
shared_ptr& operator=(unique_ptr<Other, D>&& up);
void swap(shared_ptr& sp);
void reset();
template<class Other>
void reset(Other *ptr);
template<class Other, class D>
void reset(Other *ptr, D dtor);
template<class Other, class D, class A>
void reset(Other *ptr, D dtor, A alloc);
Ty *get() const;
Ty& operator*() const;
Ty *operator->() const;
long use_count() const;
bool unique() const;
operator bool() const; template<class Other>
bool owner_before(shared_ptr<Other> const& ptr) const;
template<class Other>
bool owner_before(weak_ptr<Other> const& ptr) const;
template<class D, class Ty>
D* get_deleter(shared_ptr<Ty> const& ptr);
};

读书笔记_Effective_C++_条款四十五:运用成员函数模板接受所有兼容类型的更多相关文章

  1. 读书笔记_Effective_C++_条款四十九:了解new_handler的行为

    本章开始讨论内存分配的一些用法,C/C++内存分配采用new和delete.在new申请内存时,可能会遇到的一种情况就是,内存不够了,这时候会抛出out of memory的异常.有的时候,我们希望能 ...

  2. 读书笔记_Effective_C++_条款四十八:了解模板元编程

    作为模板部分的结束节,本条款谈到了模板元编程,元编程本质上就是将运行期的代价转移到编译期,它利用template编译生成C++源码,举下面阶乘例子: template <int N> st ...

  3. 读书笔记_Effective_C++_条款四十六:需要类型转换时请为模板定义非成员函数

    这个条款可以看成是条款24的续集,我们先简单回顾一下条款24,它说了为什么类似于operator *这样的重载运算符要定义成非成员函数(是为了保证混合乘法2*SomeRational或者SomeRat ...

  4. 读书笔记_Effective_C++_条款四十四:将与参数无关的代码抽离template

    标题上说“将与参数无关的代码抽离template”,这里的参数既可以指类型,也可以是非类型,我们先来看看非类型的情况. 假定我们要为矩阵写一个类,这个矩阵的行列元素个数相等,是一个方阵,因而我们可以对 ...

  5. 读书笔记_Effective_C++_条款四十二:了解typename的双重意义

    顾名思义,typename有双重含意.只要你用过template,那么第一重含意一定知道,那就是声明模板的时候,我们既可以这样写: template <class T> 也可以这样写 te ...

  6. 读书笔记_Effective_C++_条款二十五: 考虑写出一个不抛出异常的swap函数

    在之前的理论上调用对象的operator=是这样做的 void swap(A& x) { std::swap(a, x.a); } A& operator=(const A& ...

  7. 读书笔记_Effective_C++_条款四十:明智而审慎地使用多重继承

    多重继承是一种比较复杂的继承关系,它意味着如果用户想要使用这个类,那么就要对它的父类也了如指掌,所以在项目中会带来可读性的问题,一般我们都会尽量选择用单继承去替代它. 使用多重继承过程容易碰到的问题就 ...

  8. 读书笔记_Effective_C++_条款三十五:考虑virtual函数以外的其他选择

    举书上的例子,考虑一个virtual函数的应用实例: class GameCharacter { private: int BaseHealth; public: virtual int GetHea ...

  9. 读书笔记_Effective_C++_条款三十四:区分接口继承和实现继承

    这个条款书上内容说的篇幅比较多,但其实思想并不复杂.只要能理解三句话即可,第一句话是:纯虚函数只继承接口:第二句话是:虚函数既继承接口,也提供了一份默认实现:第三句话是:普通函数既继承接口,也强制继承 ...

随机推荐

  1. java5 ReadWriteLock用法--读写锁实现

    读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由jvm自己控制的,你只要上好相应的锁即可.如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁:如果你的代码修改数据,只能有一 ...

  2. PHP读取日志里数据方法理解

    需要函数: fopen($file, "r")打开文件 fgets($file, 1024 * 10)读取一行,注意设置字节数大小,默认的1024B可能太小了 strpos($lo ...

  3. atitit 提升数据库死锁处理总结

    atitit 提升数据库死锁处理总结 正常的来说,锁上都是自动的..不用官.. 正常来讲,insert时不需要加rowlock,就默认是rowlock了, #-----锁的自动转换原理.(正常的不用理 ...

  4. paip.数组以及集合的操作uapi java php python总结..

    paip.数组以及集合的操作uapi 作者Attilax  艾龙,  EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http://blog.csdn.net/att ...

  5. PHP类与面向对象(二)

    构造函数和析构函数 构造函数PHP 5 允行开发者在一个类中定义一个方法作为构造函数.具有构造函数的类会在每次创建新对象时先调用此方法,所以非常适合在使用对象之前做一些初始化工作.如果子类中定义了构造 ...

  6. 解决连接MySql速度慢的方法

    最近在Linux服务器上安装MySql5后,本地使用客户端连MySql速度超慢,本地程序连接也超慢.解决方法:在配置文件my.cnf的[mysqld]下加入skip-name-resolve. 原因是 ...

  7. Android 第三方开源库收集整理(转)

    原文地址:http://blog.csdn.net/caoyouxing/article/details/42418591 Android开源库 自己一直很喜欢Android开发,就如博客签名一样,  ...

  8. Scala 深入浅出实战经典 第65讲:Scala中隐式转换内幕揭秘、最佳实践及其在Spark中的应用源码解析

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...

  9. python环境中运行程序

    运行Python程序,我们比较常用的是直接在Windows命令提示窗口或者Linux终端或shell窗口中,直接:Python *.py,或者在Linux环境下,在投不中,加入: #!/usr/bin ...

  10. Android学习笔记----解决“com.android.dex.DexIndexOverflowException: method ID not in [0, 0xffff]: 65536”问题

    同时在工程中引入了多个第三方jar包,导致调用的方法数超过了android设定的65536个(DEX 64K problem),进而导致dex无法生成,也就无法生成APK文件. 解决办法如下: 1.谷 ...