当前位置: 首页 > article >正文

条款43:学习处理模板化基类内的名称(Know how to access names in templatized base classes)

条款43:学习处理模板化基类内的名称

假设需要编写一个可以向多个不同的公司发送消息的应用程序。

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:
    ... // ctors, dtor, 等等.
    void sendClear(const MsgInfo& info)
    {
        std::string msg;
        ...// 通过info创建消息;
        Company c;
        c.sendCleartext(msg);
    }
    void sendSecret(const MsgInfo& info) // 
    {
        ... // 与sendClear类似,但会调用c.sendEncrypted
    } 
};

假设我们有时想在每次发送消息时记录一些信息:

template<typename Company>
class LoggingMsgSender : public MsgSender<Company> {
public:
    ... // ctors, dtor, 等.
    void sendClearMsg(const MsgInfo& info){
        ...//在日志中写入"发送前"信息;
        //编译器会报错:sendClear不存在
        sendClear(info); // 调用基类函数;无法编译通过!!
        ...//在日志中写入"发送后"信息;
    }
    ...
};

为了让问题具体化,假设我们有一个类CompanyZ,它坚持使用加密通信:

class CompanyZ { // 这个类没有提供sendCleartext函数
public: 
 ...
 void sendEncrypted(const std::string& msg);
 ...
};

template<> // 全特化
//基类模板可以进行特化,而且这种特化可能不会提供与通用模板相同的接口
class MsgSender<CompanyZ> { // 没有sendCleartext,其他都和泛化的版本一样
public: 
    ... 
    void sendSecret(const MsgInfo& info)
    {
        ...
    }
};

回想一下前面的类模板:

//当基类是MsgSender<CompanyZ>时,此代码没有意义
template<typename Company>
class LoggingMsgSender : public MsgSender<Company> {
public:
    ...
    void sendClearMsg(const MsgInfo& info){...;sendClear(info); ...;
    ...
};

当我们从面向对象C++过渡到模板C++时,继承体系似乎失效了。需要重启它:

  1. 可以使用“this->”调用基类函数:
template<typename Company>
class LoggingMsgSender : public MsgSender<Company> {
public:
    ...
    void sendClearMsg(const MsgInfo& info)
    {
        ...//在日志中写入"发送前"信息;
        this->sendClear(info); // OK!假设sendClear将被继承
        ...//在日志中写入"发送后"信息;
    }
    ...
};
  1. 可以使用using声明
template<typename Company>
class LoggingMsgSender : public MsgSender<Company> {
public:
    using MsgSender<Company>::sendClear; // 告诉编译器,请它假设基类里有sendClear
    ... 
    void sendClearMsg(const MsgInfo& info)
    {
        ..
        sendClear(info); // OK!!假设sendClear将被继承
        ... 
    }
    ...
};
  1. 要明确指定被调用的函数在基类中有定义:
template<typename Company>
class LoggingMsgSender : public MsgSender<Company> {
public:
    ...
    void sendClearMsg(const MsgInfo& info)
    {
        ...
        MsgSender<Company>::sendClear(info); // OK!!假设sendClear将被继承
        ... 
    } 
    ...
    //这通常是解决该问题最不理想的方法,因为如果被调用的函数是虚函数,显式限定将关闭虚绑定行为。
};

这些方法都做了同样的事情:向编译器承诺,基类模板的任何后续特化都将支持通用模板提供的这些接口。如果违反了承诺,编译将会失败:

LoggingMsgSender<CompanyZ> zMsgSender;
MsgInfo msgData;
... // 将info放入msgData
zMsgSender.sendClearMsg(msgData); // 错误! 编译失败

:C++的政策倾向于早期诊断,所以当从模板实例化这些类时,它假设自己基类一无所知。(也就是强迫程序员提供更多的信息(或承诺))。

总结:可在派生类的模板内通过“this- >”、using声明、或通过显式基类限定完成成员名称的指定。


http://www.kler.cn/a/460913.html

相关文章:

  • AcWing练习题:差
  • redux react-redux @reduxjs/toolkit
  • OpenLinkSaas使用手册-待办事项和通知中心
  • SQL-leetcode-196. 删除重复的电子邮箱
  • Colyseus 与 HTTP API 的集成
  • 闪存知识科普-基本储存单元结构
  • Java中如何实现线程安全的单例模式?
  • raspberrypi-kernel
  • Ps:将数据组作为文件导出
  • RabbitMQ实现生产者消费者
  • 使用react和redux构建一个简单的计数器
  • RP2040 C SDK I2C外设使用
  • Docker容器镜像制作
  • 正则表达式介绍和python中的简单使用
  • 大中厂面试经验分享:如何使用消息队列(MQ)解决系统问题
  • 科技风杂志科技风杂志社科技风编辑部2024年第36期目录
  • 【优选算法】有效三角形的个数
  • SpringBoot集成ECDH密钥交换
  • Linux C/C++编程-网络程序架构与套接字类型
  • 【Java 新特性】深入浅出 Java Lambda 表达式
  • vim里搜索关键字
  • 【Windows】Windows系统查看目录中子目录占用空间大小
  • YK人工智能(二)——万字长文了解深度学习环境配置
  • grep如何打印行数
  • C++线程池的使用
  • 智能商业分析 Quick BI