读书笔记_Effective_C++_条款四十五:运用成员函数模板接受所有兼容类型
比如有一个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++_条款四十五:运用成员函数模板接受所有兼容类型的更多相关文章
- 读书笔记_Effective_C++_条款四十九:了解new_handler的行为
本章开始讨论内存分配的一些用法,C/C++内存分配采用new和delete.在new申请内存时,可能会遇到的一种情况就是,内存不够了,这时候会抛出out of memory的异常.有的时候,我们希望能 ...
- 读书笔记_Effective_C++_条款四十八:了解模板元编程
作为模板部分的结束节,本条款谈到了模板元编程,元编程本质上就是将运行期的代价转移到编译期,它利用template编译生成C++源码,举下面阶乘例子: template <int N> st ...
- 读书笔记_Effective_C++_条款四十六:需要类型转换时请为模板定义非成员函数
这个条款可以看成是条款24的续集,我们先简单回顾一下条款24,它说了为什么类似于operator *这样的重载运算符要定义成非成员函数(是为了保证混合乘法2*SomeRational或者SomeRat ...
- 读书笔记_Effective_C++_条款四十四:将与参数无关的代码抽离template
标题上说“将与参数无关的代码抽离template”,这里的参数既可以指类型,也可以是非类型,我们先来看看非类型的情况. 假定我们要为矩阵写一个类,这个矩阵的行列元素个数相等,是一个方阵,因而我们可以对 ...
- 读书笔记_Effective_C++_条款四十二:了解typename的双重意义
顾名思义,typename有双重含意.只要你用过template,那么第一重含意一定知道,那就是声明模板的时候,我们既可以这样写: template <class T> 也可以这样写 te ...
- 读书笔记_Effective_C++_条款二十五: 考虑写出一个不抛出异常的swap函数
在之前的理论上调用对象的operator=是这样做的 void swap(A& x) { std::swap(a, x.a); } A& operator=(const A& ...
- 读书笔记_Effective_C++_条款四十:明智而审慎地使用多重继承
多重继承是一种比较复杂的继承关系,它意味着如果用户想要使用这个类,那么就要对它的父类也了如指掌,所以在项目中会带来可读性的问题,一般我们都会尽量选择用单继承去替代它. 使用多重继承过程容易碰到的问题就 ...
- 读书笔记_Effective_C++_条款三十五:考虑virtual函数以外的其他选择
举书上的例子,考虑一个virtual函数的应用实例: class GameCharacter { private: int BaseHealth; public: virtual int GetHea ...
- 读书笔记_Effective_C++_条款三十四:区分接口继承和实现继承
这个条款书上内容说的篇幅比较多,但其实思想并不复杂.只要能理解三句话即可,第一句话是:纯虚函数只继承接口:第二句话是:虚函数既继承接口,也提供了一份默认实现:第三句话是:普通函数既继承接口,也强制继承 ...
随机推荐
- docker解决数据存储问题的方案
现在docker在云计算领域发展的势头很猛,各个公司不论大小都开始研究这个开源工具和技术,围绕docker的开源项目和创业公司也多如牛毛,就是一个简单管理container的web ui都有很多开源项 ...
- [Java拾遗二]Tomact及Http 部分总结.
前言: 刚好今天回来的很早, 总结下 Tomcat及Http的基础知识. 1, Tomcat web相关概念 web:网页的意思,网页资源包括服务器上的所有资源. ...
- IOS行货自动打包
通常打包采用xcodebuild和xcrun两个命令,xcodebuild负责编译,xcrun负责将app打成ipa. 常见步骤如下: 1.清理工程 /usr/bin/xcodebuild -ta ...
- Maven学习总结(九)——使用Nexus搭建Maven私服
一.搭建nexus私服的目的 为什么要搭建nexus私服,原因很简单,有些公司都不提供外网给项目组人员,因此就不能使用maven访问远程的仓库地址,所以很有必要在局域网里找一台有外网权限的机器,搭建n ...
- 如何管理和记录 SSIS 各个 Task 的开始执行时间和结束时间以及 Task 中添加|删除|修改的记录数
开篇语 在这篇日志中 如何在 ETL 项目中统一管理上百个 SSIS 包的日志和包配置框架 我介绍到了包级别的日志管理框架,那么这个主要是针对包这一个层级的 Log 信息,包括包开始执行和结束时间,以 ...
- [原创]上海好买基金招高级Java技术经理/运维主管/高级无线客户端开发等职位(内推)
[原创]上海好买基金招高级Java技术经理/运维主管/高级无线客户端开发等职位(内推) 内部推荐职位 高级JAVA技术经理: 岗位职责: 负责项目管理(技术方向),按照产品开发流 ,带领研发团队,制定 ...
- 通过weburl 启动windows程序
1. 注册表修改 建立一个reg文件 执行导入 以RunLocal协议为例子 Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\RunL ...
- use IFS in bash
function dfd() { #http://www.cnblogs.com/hunterfu/archive/2010/02/23/1672129.html IFS=$'\n' for i in ...
- NoteExpress 自定义参考文献输出样式
最近在写硕士论文,样式中没有符合要求的,于是就自己来编辑下样式文件,下文是网上搜集的关于自定义参考文献的一篇教程,可以参考. 一.分隔符|和连接符*的使用 (1)连接符 * 主要作用是“建立依赖关系” ...
- LPC43xx SGPIO Pattern Match Mode
模式匹配 所有位串均具有模式匹配功能. 该功能可用于检测启动代码等.要使用该功能,则必须用需匹配的模式来对REG_SS 编程 (请注意, POS 达到零时 REG_SS 不会与 REG 交换!) M ...