背景是这样的,有两个不同的公司,然后想设计一个MessageSender,为这两个公司发送不同的消息,既支持明文发送SendClearText,也支持密文发送SendEncryptedText。一种思路是采用动态绑定的方法,定义一个BasicMessageSender,里面有两个方法,分别是发送明文和密文的虚函数,然后定义它的子类MessageSenderForCompanyA,以及MessageSenderForCompanyB,在这两个子类里面覆盖发送明文和密文的虚函数,从而达到根据不同公司发送不同消息的目的。

但这里我们想换一种思路,使用静态多态的方法来实现,静态多态就是模板的技术了,代码如下:

 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++_条款四十三:学习处理模板化基类的名称的更多相关文章

  1. 读书笔记_Effective_C++_条款三十三:避免遮掩继承而来的名称

    名称的遮掩可以分成变量的遮掩与函数的遮掩两类,本质都是名字的查找方式导致的,当编译器要去查找一个名字时,它一旦找到一个相符的名字,就不会再往下去找了,因此遮掩本质上是优先查找哪个名字的问题. 而查找是 ...

  2. 读书笔记 effective c++ Item 43 了解如何访问模板化基类中的名字

    1. 问题的引入——派生类不会发现模板基类中的名字 假设我们需要写一个应用,使用它可以为不同的公司发送消息.消息可以以加密或者明文(未加密)的方式被发送.如果在编译阶段我们有足够的信息来确定哪个信息会 ...

  3. 读书笔记_Effective_C++_条款四十七:请使用trait classes来表示类型信息

    这一条款主要来讨论模板中迭代器的属性iterator_category,它可以通过类似于vector<int>::iterator::iterator_category的方式来取得. 到这 ...

  4. 读书笔记_Effective_C++_条款四十一:了解隐式接口和编译期多态

    从本条款开始,就进入了全书的第七部分:模板与泛型编程.模板与泛型在C++中是非常重要的部分,还记得本书第一章时,把C++视为一个联邦,它由四个州政府组成,其中一个政府就是模板与泛型了. 本条款是一个介 ...

  5. 读书笔记_Effective_C++_条款四十九:了解new_handler的行为

    本章开始讨论内存分配的一些用法,C/C++内存分配采用new和delete.在new申请内存时,可能会遇到的一种情况就是,内存不够了,这时候会抛出out of memory的异常.有的时候,我们希望能 ...

  6. 读书笔记_Effective_C++_条款四十八:了解模板元编程

    作为模板部分的结束节,本条款谈到了模板元编程,元编程本质上就是将运行期的代价转移到编译期,它利用template编译生成C++源码,举下面阶乘例子: template <int N> st ...

  7. 读书笔记_Effective_C++_条款四十六:需要类型转换时请为模板定义非成员函数

    这个条款可以看成是条款24的续集,我们先简单回顾一下条款24,它说了为什么类似于operator *这样的重载运算符要定义成非成员函数(是为了保证混合乘法2*SomeRational或者SomeRat ...

  8. 读书笔记_Effective_C++_条款四十五:运用成员函数模板接受所有兼容类型

    比如有一个Base类和一个Derived类,像下面这样: class BaseClass {…}; class DerivedClass : public BaseClass {…}; 因为是父类与子 ...

  9. 读书笔记_Effective_C++_条款四十四:将与参数无关的代码抽离template

    标题上说“将与参数无关的代码抽离template”,这里的参数既可以指类型,也可以是非类型,我们先来看看非类型的情况. 假定我们要为矩阵写一个类,这个矩阵的行列元素个数相等,是一个方阵,因而我们可以对 ...

随机推荐

  1. JAVA的安装与软件使用

    1.JAVA的安装与环境 2.练习: (1)初识java (2)使用eclipse输出自己的名字

  2. (谷歌浏览器等)解决css中点击input输入框时出现外边框方法【outline:medium;】

    问题:在使用谷歌浏览器,360浏览器时,点击input输入框会出现带颜色的外边框,如下图所示:

  3. js 停止事件冒泡 阻止浏览器的默认行为(阻止超连接 # )

    在前端开发工作中,由于浏览器兼容性等问题,我们会经常用到“停止事件冒泡”和“阻止浏览器默认行为”. 1..停止事件冒泡 JavaScript代码 //如果提供了事件对象,则这是一个非IE浏览器if ( ...

  4. struts2从2.2.3升级到2.3.15.1步骤

    1. 删除以下jar包 asm-3.1.jar asm-commons-3.1.jar commons-beanutils-1.7.0.jar commons-fileupload-1.2.2.jar ...

  5. Android Studio no debuggable applications解决方案2

    android studio 默认是没有开启debuggable 功能的,在tools里打开该功能即可,Tools->Android->Enable ADB Integration. 刚设 ...

  6. Java httpclient请求,解决乱码问题

    public class HttpPostRequestUtil { public HttpPostRequestUtil() { } public static String post(String ...

  7. Scala 深入浅出实战经典 第41讲:List继承体系实现内幕和方法操作源码揭秘

    Scala 深入浅出实战经典 第41讲:List继承体系实现内幕和方法操作源码揭秘 package com.parllay.scala.dataset /** * Created by richard ...

  8. mysql简单性能排查

    mysql> show variables; mysql> show processlist; mysql> show status; mysql> show global s ...

  9. 资源监控工具Spotlight-使用说明

    几年前使用过此工具,发现用于监控远程服务器,非常方面而且快捷.当前再次用于配合压力测试进行资源监控,突然想起来了,以免生疏,在此记录! 1.被监控服务器为Ubuntu server,先在服务器上创建一 ...

  10. mediawiki的管理与使用

    本文主要讲述搭建好私有的mediawiki之后,管理员可能需要用到的几个功能.   维基百科的设计思路与我以往使用的系统不太一样,以管理员模式进入之后,并没有我预想的添加wiki页面入口,和侧边栏导航 ...