Item 43:访问模板基类中的名称 Effective C++笔记
2015-10-21 13:55
471 查看
Item 43: Know how to access names in templatized base classes.
从面相对象C++转移到模板C++时,你会发现类继承在某些场合不在好使了。 比如父类模板中的名称对子类模板不是直接可见的,需要通过
因为父类模板在实例化之前其中的名称是否存在确实是不确定的,而C++偏向于早期发现问题(early diagnose),所以它会假设自己对父类完全无知。
一个
由于某种需求我们需要继承
首先要说明这里我们创建了新的方法
避免了隐藏父类中的名称,见Item 33;也避免了重写父类的非虚函数,见Item
36。
编译错误发生的原因是编译器不知道父类
而在解析子类
为了让这个逻辑更加明显,假设我们需要一个公司
template specialization)。 我们叫它全特化是因为
现在前面的编译错误就更加明显了:如果
既然模板父类中的名称在子类中不是直接可见的,我们来看如何访问这些名称。这里介绍三种办法:
父类方法的调用语句前加
这样编译器会假设
把父类中的名称使用
33中用过,那里是为了在子类中访问被隐藏的父类名称, 而这里是因为编译器不会主动去搜索父类的作用域。
最后一个办法是在调用时显式指定该函数所在的作用域(父类):
这个做法不是很好,因为显式地指定函数所在的作用域会禁用虚函数特性。万一
子类模板无法访问父类模板中的名称是因为编译器不会搜索父类作用域,上述三个办法都是显式地让编译器去搜索父类作用域。 但如果父类中真的没有
除非注明,本博客文章均为原创,转载请以链接形式标明本文地址: http://harttle.com/2015/09/10/effective-cpp-43.html
从面相对象C++转移到模板C++时,你会发现类继承在某些场合不在好使了。 比如父类模板中的名称对子类模板不是直接可见的,需要通过
this->前缀、
using或显式地特化模板父类来访问父类中的名称。
因为父类模板在实例化之前其中的名称是否存在确实是不确定的,而C++偏向于早期发现问题(early diagnose),所以它会假设自己对父类完全无知。
编译错的一个例子
一个MsgSender需要给多个
Company发送消息,我们希望在编译期进行类型约束,于是选择了模板类来实现
MsgSender。
[code]template<typename Company> class MsgSender{ public: void sendClear(const MsgInfo& info){...} // 发送明文消息 void sendSecret(const MsgInfo& info){...} // 发送密文消息 };
由于某种需求我们需要继承
MsgSender,比如需要在发送前纪录日志:
[code]template<typename Company> class LoggingMsgSender: public MsgSender<Company>{ public: void sendClearMsg(const MsgInfo& info){ // 存储一些日志 sendClear(info); // 编译错! } };
首先要说明这里我们创建了新的方法
sendClearMsg而不是直接重写
sendClear是一个好的设计,
避免了隐藏父类中的名称,见Item 33;也避免了重写父类的非虚函数,见Item
36。
编译错误发生的原因是编译器不知道父类
MsgSender<Company>中是否有一个
sendClear,因为只有当
Company确定后父类才可以实例化。
而在解析子类
LoggingMsgSender时父类
MsgSender还没有实例化,于是这时根本不知道
sendClear是否存在。
为了让这个逻辑更加明显,假设我们需要一个公司
CompanyZ,由于该公司的业务只能发送密文消息。所以我们特化了
MsgSender模板类:
[code]template<> class MsgSender<CompanyZ>{ public: void sendSecret(const MsgInfo& info){...} // 没有定义sendClear() };
template<>意味着这不是一个模板类的定义,是一个模板类的全特化(total
template specialization)。 我们叫它全特化是因为
MsgSender没有其它模板参数,只要
CompanyZ确定了
MsgSender就可以被实例化了。
现在前面的编译错误就更加明显了:如果
MsgSender的模板参数
Company == CompanyZ, 那么
sendClear()方法是不存在的。这里我们看到在模板C++中继承是不起作用的。
访问模板父类中的名称
既然模板父类中的名称在子类中不是直接可见的,我们来看如何访问这些名称。这里介绍三种办法:
this指针
父类方法的调用语句前加this->:
[code]template<typename Company> class LoggingMsgSender: public MsgSender<Company>{ public: void sendClearMsg(const MsgInfo& info){ ... this->sendClear(info); } };
这样编译器会假设
sendClear是继承来的。
using 声明
把父类中的名称使用using声明在子类中。该手法我们在Item
33中用过,那里是为了在子类中访问被隐藏的父类名称, 而这里是因为编译器不会主动去搜索父类的作用域。
[code]template<typename Company> class LoggingMsgSender: public MsgSender<Company>{ public: using MsgSender<Company>::sendClear; void sendClearMsg(const MsgInfo& info){ ... sendClear(info); } };
using语句告诉编译器这个名称来自于父类
MsgSender<Company>。
调用时声明
最后一个办法是在调用时显式指定该函数所在的作用域(父类):[code]template<typename Company> class LoggingMsgSender: public MsgSender<Company>{ public: void sendClearMsg(const MsgInfo& info){ ... MsgSender<Company>::sendClear(info); } };
这个做法不是很好,因为显式地指定函数所在的作用域会禁用虚函数特性。万一
sendClear是个虚函数呢?
子类模板无法访问父类模板中的名称是因为编译器不会搜索父类作用域,上述三个办法都是显式地让编译器去搜索父类作用域。 但如果父类中真的没有
sendClear函数(比如模板参数是
CompanyZ),在后续的编译中还是会抛出编译错误。
除非注明,本博客文章均为原创,转载请以链接形式标明本文地址: http://harttle.com/2015/09/10/effective-cpp-43.html
相关文章推荐
- 每日总结关于c语言中不会问题的解答
- 关于一个通俗易懂的FFT的C语言实现教程
- 剑指offer学习--初级c++面试题
- 3D打印切片软件Cura及CuraEngine原理分析
- C++实现split函数
- C++ const
- C++中引用(&)的用法和应用实例
- 数组中不相邻元素的最大和
- C语言在屏幕上输出杨辉三角
- C语言---return(我的工程笔记本)
- 设计模式之外观模式
- C++类的线程函数为什么要加static修饰
- c++数组、字符串操作
- C++ 引用计数
- c语言获取当前可执行文件的执行路径个文件名
- C++的一些模块学习!
- C++学习笔记5 - 循环与关系表达式
- c++ List、Vector、Stack、Queue使用
- C++的一些学习技巧!
- C++primer学习:拷贝控制(6):编写简化的string类