《Effective C++》学习笔记——条款43
2016-10-13 16:00
330 查看
七、模板与泛型编程
条款43:学习处理模板化基类内的名称
开始,先写一个程序,它能够传送信息到若干不同的公司去。信息要么就是被译成密码,要么就是未经过加工的文字。如果在编译期内,我们拥有足够的信息来决定哪一个信息传递到哪一家公司,就可以采用template的方法。
class CompanyA { public: ... void sendCleartext(const std::string& msg); void sendEncrypted(const std::string& msg); ... }; class CompanyB { public: ... void sendCleartext(const std::string& msg); void sendEncrypted(const std::string& msg); ... }; ... // 针对其他公司设计的类 class MsgInfo { ... }; // 保存信息,以备将来产生信息 template<typename Company> class MsgSender { public: ... // 构造、析构函数等 void sendClear(const MsgInfo& info) { std::string msg; Company c; c.sendCleartext(msg); } void sendSecret(const MsgInfo& info) { std::string msg; Company c; c.sendEncrypted(msg); } };
这个做法是可以的,而且如果我们需要在送出信息前打印某些信息也可以轻松在派生类中加上。
template<typename Company> class LoggingMsgSender: public MsgSender<Company> { public: ... void sendClearMsg(const MsgInfo& msg) { // 此处打印发送前的信息 sendClear(info); // 此处打印发送后的信息 } ... };
但是在上面代码中,
sendClear调用了基类的函数,会无法通过编译,编译器提示你
sendClear不存在。
Why?明明在基类里!
因为,当编译器遇到
class template LoggingMsgSender定义式的时候,并不知道它继承了什么样的类。
显然它继承了
MsgSender<Company>,但是这里面的
Company是一个tamplate参数,不到后来
LoggingMsgSender被具现化,并不能确切的知道它是什么,因此就无法知道它是否有个
sendClear函数。
更深的来说,是因为C++知道
base class templates有可能被特化,而特化版本可能不提供一般性template相同的接口。
关于特化
template<> class MsgSender<CompanyZ> { public: ... void sendSecret(const MsgInfo& info) { ... } ... };
类前的 template<> 语法 表示这既不是template也不是标准class,而是一个特化版本的MsgSender template,只有在template实参是CompanyZ时调用。
这就是模板全特化,通过特化,可以不提供一般性template相同的接口。
因此,当CompanyZ在上面调用 sendClear时,就没有这个函数。
话题转过来,如何解决呢?
我们需要让C++ 进入templatized base class观察。
这里有三个办法:
在基类函数调用动作之前加上
this->
将
sendClear(info);改成
this->sendClear(info);
使用
using声明式
告诉编译器,假设
sendClear位于基类内
在
LoggingMsgSender类内添加
using MsgSender<Company>::senderClear;
指出被调用的函数位于基类内
将
sendClear(info);改成
MsgSender<Company>::sendClear(info);
但这个办法不是很好,因为当被调用的是virtual函数时,明确的资格修饰会关闭virtual绑定行为。
其实,上面所有的做法都是在保证:base class template的任何特化版本都将支持一般(泛化)版本所提供的接口。
请记住
可在derived class template内通过 “
this->“指涉
base class templates内的成员名称,或通过直接写出的 “
base class 资格修饰符” 完成。
相关文章推荐
- 《Effective C++》学习笔记条款15 在资源管理类中提供对原始资源的访问
- 《Effective C++》学习笔记——条款37
- 《Effective C++》学习笔记——条款15
- Effective C++——条款43(第7章)
- 《Effective C++》学习笔记条款01 视C++为一个语言联邦
- 《Effective C++》学习笔记条款08 别让异常逃离析构函数
- 《Effective C++》学习笔记条款16 成对使用new和delete时要采取相同形式
- 《Effective C++》学习笔记条款24 若所有参数皆需类型转换,请为此采用non-member函数
- 《Effective C++ 》学习笔记——条款05
- 《Effective C++》学习笔记——条款26
- 《Effective C++》学习笔记——条款29
- 《Effective C++》学习笔记条款02 尽量以const,enum,inline替换#define
- 《Effective C++》学习笔记条款09 决不让构造和析构过程中调用virtual函数
- 《Effective C++》学习笔记条款27 尽量少做转型动作
- 《Effective C++》学习笔记——条款40
- 《Effective C++》学习笔记——条款41
- 《Effective C++》学习笔记——条款28
- Effective C++ 条款43 学习处理模板化基类内的名称
- 《Effective C++》学习笔记条款03 尽可能使用const
- 《Effective C++》学习笔记条款10 令operator= 返回一个reference to *this