读书笔记_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++_条款三十四:区分接口继承和实现继承
这个条款书上内容说的篇幅比较多,但其实思想并不复杂.只要能理解三句话即可,第一句话是:纯虚函数只继承接口:第二句话是:虚函数既继承接口,也提供了一份默认实现:第三句话是:普通函数既继承接口,也强制继承 ...
随机推荐
- IE11下Forms身份认证无法保存Cookie的问题
ASP.NET中使用Forms身份认证常见的做法如下: 1. 网站根目录下的Web.config添加authentication节点 <authentication mode="For ...
- iOS Runtime原理及使用
runtime简介 因为Objc是一门动态语言,所以它总是想办法把一些决定工作从编译连接推迟到运行时.也就是说只有编译器是不够的,还需要一个运行时系统 (runtime system) 来执行编译后的 ...
- 简单理解ECMAScript2015中的Promise
ECMAScript6中新增了Promise对象, 所谓Promise对象,即代表着一个还未完成,但将来某时会完成的操作(通常是异步操作).使用Promise对象,我们就可以避免陷入函数层层嵌套的‘回 ...
- C语言实现二叉树-利用二叉树统计单词数目
昨天刚参加了腾讯2015年在线模拟考: 四道大题的第一题就是单词统计程序的设计思想: 为了记住这一天,我打算今天通过代码实现一下: 我将用到的核心数据结构是二叉树: (要是想了解简单二叉树的实现,可以 ...
- paip.自适应网页设计 跟 响应式 设计的区别跟原理and实践总结
paip.自适应网页设计 跟 响应式 设计的区别跟原理and实践总结 响应式Web设计(Responsive Web design)的理念是: 1 #-----------自适应布局VS响应式布局 2 ...
- paip.代码生成器数据源格式最佳实践
paip.代码生成器数据源格式最佳实践 需要满足几个条件. 1.基于文本 2.容易的编辑.. 3,容易的代码解析. 这样,常用的设计工具powerdesign cdm pdm就排除兰...cdm虽然是 ...
- OpenGL学习进程(11)第八课:颜色绘制的详解
本节是OpenGL学习的第八个课时,下面将详细介绍OpenGL的颜色模式,颜色混合以及抗锯齿. (1)颜色模式: OpenGL支持两种颜色模式:一种是RGBA,一种是颜色索引模式. R ...
- 在jsp中重复定义了两个相同id的标签导致的错误
jQuery做前台开发的程序有一个页面在IE11和谷歌浏览器下都没有问题,但是在XP的IE8下运行就报错: 后来发现是定义了两个相同id的标签所致. 在icCard.jsp中定义的标签: 在carIn ...
- Spring 4.3.2下实现http多次断点下载
其实跟 spring 无关,如果是直接下载资源很多 web sever 不用程序就直接实现了断点. 但我们的应用是 download?url=xxxx 这种方式 下载资源由 download 来负责, ...
- Gradle中ProGuard的配置
好久没有写博客了…元旦前赶紧写一篇吧… 这些日子琢磨了一下gradle.对比起maven确实在配置上灵活很多,对groovy的支持可以更容易的自定义任务. 由于最近的几个项目中都使用到了moco这个开 ...