读书笔记_Effective_C++_条款四十三:学习处理模板化基类的名称
2014-03-26 00:00
543 查看
背景是这样的,有两个不同的公司,然后想设计一个MessageSender,为这两个公司发送不同的消息,既支持明文发送SendClearText,也支持密文发送SendEncryptedText。一种思路是采用动态绑定的方法,定义一个BasicMessageSender,里面有两个方法,分别是发送明文和密文的虚函数,然后定义它的子类MessageSenderForCompanyA,以及MessageSenderForCompanyB,在这两个子类里面覆盖发送明文和密文的虚函数,从而达到根据不同公司发送不同消息的目的。
但这里我们想换一种思路,使用静态多态的方法来实现,静态多态就是模板的技术了,代码如下:
CompanyA与CompanyB有各自的发送函数,然后有一个模板类MsgSender,这个模板待确定的参数是T,可以是CompanyA或者CompanyB,这样就可以在定义MsgSender时,比如MsgSender<CompanyA>或者MsgSender<CompanyB>,指定到底调用的哪个公司的发送函数了,这是在编译期就可以确定下来的事情。
但现在有一个新问题,那就是我们希望在执行发送函数之前,还是加上日志比较好,这样我们就继承了MsgSender,定义了一个新类MsgSenderWithLog,在这里定义了一个新的函数SendClearTextWithLog,在这个函数里面调用了父类的SendClearText。
对于这段代码,其实思路还是挺清晰的,但问题是有的编译器会编不过这行代码(VS2008以后的版本的都是可以的,之前的版本没试),为什么?
这是因为在模板技术中存在全特化的概念,比如C公司,这个公司根本不想发送明文,也就是说它只有SendEncryptedText()接口,没有SendClearText()。为了使我们的静态多态仍然可用,我们这样定义只适用于C公司的MsgSender:
这时候如果去调用:
这样编译器会报找到SendClearText()的错。正是因为有的编译器考虑到了全特化模板版本可以与普通版本不同,所以在有继承关系存在时,对直接调用父类的函数给出了不支持的error。但这个error是与编译器相关的,不是必然出现的。
为了让更多的编译器放弃这种全特化的忧虑,书上提供了三种解决方法:
方法一: 将
方法二:
在子类中声明using MsgSender<T>::SendClearText;
编译器报error本质是不进行模板父类域的查找,所以这里using了父类的一个函数名,强制编译器对之进行查找。
方法三:
将SendClearText()指明为MsgSender<T>::SendClearText()。
最后总结一下:
可在derived class template内通过“this->”指涉base class templates内的成员名称,或藉由一个明白写出的“base class资格修饰符”完成。
但这里我们想换一种思路,使用静态多态的方法来实现,静态多态就是模板的技术了,代码如下:
class CompanyA { public: void SendClearText(){} void SendEncryptedText(){} }; class CompanyB { public: void SendClearText(){} void SendEncrypedText(){} }; template <class T> class MsgSender { public: void SendClearText(){} }; template <class T> class MsgSenderWithLog: public MsgSender<T> { public: void SendClearTextWithLog() { // Logs SendClearText(); // 有的编译器会编不过这段代码 } }; int main() { MsgSenderWithLog<CompanyA> MsgSender; MsgSender.SendClearTextWithLog(); }
CompanyA与CompanyB有各自的发送函数,然后有一个模板类MsgSender,这个模板待确定的参数是T,可以是CompanyA或者CompanyB,这样就可以在定义MsgSender时,比如MsgSender<CompanyA>或者MsgSender<CompanyB>,指定到底调用的哪个公司的发送函数了,这是在编译期就可以确定下来的事情。
但现在有一个新问题,那就是我们希望在执行发送函数之前,还是加上日志比较好,这样我们就继承了MsgSender,定义了一个新类MsgSenderWithLog,在这里定义了一个新的函数SendClearTextWithLog,在这个函数里面调用了父类的SendClearText。
对于这段代码,其实思路还是挺清晰的,但问题是有的编译器会编不过这行代码(VS2008以后的版本的都是可以的,之前的版本没试),为什么?
这是因为在模板技术中存在全特化的概念,比如C公司,这个公司根本不想发送明文,也就是说它只有SendEncryptedText()接口,没有SendClearText()。为了使我们的静态多态仍然可用,我们这样定义只适用于C公司的MsgSender:
class CompanyC { public: void SendEncryptedText(){} }; template <> class MsgSender<CompanyC> { public: void SendEncryptedText(){} };
这时候如果去调用:
MsgSenderWithLog<CompanyC> MsgSenderC; MsgSenderC.SendClearTextWithLog(); //编译器无法通过编译
这样编译器会报找到SendClearText()的错。正是因为有的编译器考虑到了全特化模板版本可以与普通版本不同,所以在有继承关系存在时,对直接调用父类的函数给出了不支持的error。但这个error是与编译器相关的,不是必然出现的。
为了让更多的编译器放弃这种全特化的忧虑,书上提供了三种解决方法:
方法一: 将
void SendClearTextWithLog() { // Logs SendClearText(); // 有的编译器会编不过这段代码 } 改成 void SendClearTextWithLog() { // Logs this->SendClearText(); // 这下能编译通过了 }
方法二:
在子类中声明using MsgSender<T>::SendClearText;
编译器报error本质是不进行模板父类域的查找,所以这里using了父类的一个函数名,强制编译器对之进行查找。
方法三:
将SendClearText()指明为MsgSender<T>::SendClearText()。
最后总结一下:
可在derived class template内通过“this->”指涉base class templates内的成员名称,或藉由一个明白写出的“base class资格修饰符”完成。
相关文章推荐
- Effective C++笔记_条款43 学习处理模板化基类内的名称
- Effective C++ Item 43 学习处理模板化基类内的名称
- Effective C++ Item 43 学习处理模板化基类内的名称
- Effective C++笔记_条款43 学习处理模板化基类内的名称
- 条款43:学习处理模板化基类内的名称
- Effective C++学习笔记_条款43:学习处理模板化基类内的名称
- 条款43:学习处理模板化基类内的名称
- effective C++ 条款 43:学习处理模板化基类内的名称
- Effective C++ 条款43 学习处理模板化基类内的名称
- 《Effective C++》读书笔记之item43:学习处理模板化基类内的名称
- Effective C++:条款43:学习处理模板化基类内的名称
- Effective C++ -----条款43:学习处理模板化基类内的名称
- 条款43:学习处理模板化基类内的名称
- Effective C++ 43条 处理模板化基类内的名称
- 《Effective C++》:条款43:学习处理模板化基类内的名称
- 条款43:学习处理模板化基类内的名称
- 条款43:学习处理模板化基类内的名称
- 条款43:学习处理模板化基类的名称
- C++之 模板化基类 的名称处理
- 读书笔记_Effective_C++_条款三十三:避免遮掩继承而来的名称