《Effective C++》:条款43:学习处理模板化基类内的名称

条款43:学会处理模板化基类内的名称 1、问题引入 假如我们需要这样一个程序:它能够传递不同的信息到不同的公司去。所需要传递的信息分为两类:加密信息、未加密的文本信息。 针对上述需求我们可以采用template的解法: 首先写多个公司类,类中有发送加密信息和未加密信息的方法。 其次写一个信息发送类模板,其模板参数为不同的公司类。这个类用于根据模板参数向不同的公司传递信息。 2、问题升级 上述解法可以解决我们所提出的需求。但是现在,... 阅读详情

[toc]
模板化的类作为基类时,有哪些要注意的地方。以一个例子说明,假设现在编写一个发送信息到不同公司的程序,信息要么译成密码,要么就是原始文字,在编译期间来决定哪一家公司发送至哪一家公司,采用template手法:

    class CompanyA{
    public:
        void sendCleartext(const std::string& msg);
        void sendEncryted(const std::string& msg);
        ……
    };
    class CompanyB{
    public:
        void sendCleartext(const std::string& msg);
        void sendEncryted(const std::string& msg);
        ……
    };
    ……//还有一些公司

    class MsgInfo{……};//用来保存信息,以备将来产生信息

    template<typename Company>
    class MsgSender{
    public:
        ……//构造析构等函数
        void sendClear(const MsgInfo& info)
        {
            std::string msg;
            //根据info产生信息
            Company c;
            c.sendCleartext(msg);
        }
        void sendSecart(const MsgInfo& info)
        {……}
    };

上面这个做法行得通,但是如果要在每次送出信息时记录日志,可以派生出derived class,加上记录的日志

    template<typename Company>
    class LoggingMsgSender: public MsgSender<Company>
    {
    public:
        ……//析构构造等
        void SendClearMsg(const MsgInfo& info)
        {
            //发送前的信息写到log
            sendClear(info);//调用base class函数,这段代码无法通过编译
            //传送后信息写到log
        }
    };

在派生类中,sendClearMsg和base class中的sendClear不同,这是个好设计,避免遮掩继承而得的名称,也避免了重新定义一个继承而得non-virtual函数。但是编译不能通过,因为编译器看不到sendClear函数。

因为当编译器遇到class template LoggingMsgSender定义式时,不知道它继承什么样的class,因为MsgSender中的Company是个参数,在LoggingMsgSender被具体化之前,无法确切知道它是什么,自然而然就不知道class MsgSender是什么,也就不知道它是否有个sendClear函数。(备注,在g++中可以使用参数 -fpermissive参数来通过编译,但是不建议使用)

为了让问题更具体化,假设现在有个class CompanyZ坚持使用加密通讯

    class CompanyZ{
    pubic:
        void sendEncryted(const std::sting& msg);
        ……
    };

CompanyZ没有sendClear函数,一般性的MsgSender template对CompanyZ并不合适,这时我们可以针对CompanyZ产生一个MsgSender特化版

    template<>
    class MsgSender<CompanyZ>{
    public:
        void sendSecret(const MsgInfo& infof)
        {……}
        ……
    };

开头的template<>表示是特化版的MsgSender template,在template实参是CompanyZ时被使用。这就是所谓的模板全特化(total template specialization):template MsgSender针对类型CompanyZ特化了,且是全面性特化,即一旦类型参数为CompanyZ,没有其他template参数可供变化了。

再来看一下刚刚的LoggingMsgSender

    template<typename Company>
    class LoggingMsgSender: public MsgSender<Company>
    {
    public:
        ……//析构构造等
        void SendClearMsg(const MsgInfo& info)
        {
            //发送前的信息写到log
            sendClear(info);//如实Company是CompanyZ,那么这个函数不存在
            //传送后信息写到log
        }
    };

如果Company=CompanyZ,那么sendClear函数就不存在。C++拒绝调用这个函数是因为它知道base class template可能被特化,而那个特化版本可能不提供和一般性template相同的接口。所以它往往拒绝在templatized base classes(模板化基类)中寻找继承而来的名称。当我们从Object Oriented C++跨进到Template C++时,继承不像以前那样畅行无阻了。

现在应该讨论一下怎么解决上面不能通过编译的问题了。我们必须有某种办法令C++“不进入templatized base class观察”的行为失效。有一下三种办法

1、在base class函数调用动作之前加上“this->”

    template<typename Company>
    class LoggingMsgSender: public MsgSender<Company>
    {
    public:
        ……//析构构造等
        void SendClearMsg(const MsgInfo& info)
        {
            //发送前的信息写到log
            this->sendClear(info);
            //传送后信息写到log
        }
    };

2、使用using声明式,有点类似**条款**33。

    template<typename Company>
    class LoggingMsgSender: public MsgSender<Company>
    {
    public:
        ……//析构构造等
        uinsg MsgSender<Company>::sendClear;//告诉编译器,假设sendClear位于base class内
        void SendClearMsg(const MsgInfo& info)
        {
            //发送前的信息写到log
            sendClear(info);//可以编译通过,假设sendClear将被继承
            //传送后信息写到log
        }
    };

补充一下,这里情况和**条款**33不同,这里不是将被掩盖的base class名称带入一个derived class作用域内,而是编译器不进入base class作用域内查找,通过using告诉编译器,请它去查找。

3、明白指出被调用的函数位于base class内

    template<typename Company>
    class LoggingMsgSender: public MsgSender<Company>
    {
    public:
        ……//析构构造等
        void SendClearMsg(const MsgInfo& info)
        {
            //发送前的信息写到log
            MsgSender<Company>::sendClear(info);//可以编译通过,假设sendClear被继承
            //传送后信息写到log
        }
    };

这种做法使用了明确资格修饰符(explicit qualification),这将会关闭virtual绑定行为。

回头再看上面三种做法,原理相同:对编译器承诺“base class template的任何特化版本都将支持其一般(泛化)版本所提供的接口”。这样的承诺是编译器在解析(parse)像LoggingMsgSender这样的derived class template时需要的。但如果这个承诺稍后没有兑现,编译器还会给事实一个公道。例如,稍后源码内:

    LoggingMsgSender<CompanyZ> zMsgSender;
    MsgInfo msgData;
    zMsgSender.sendClearMsg(msgData);//错误,无法编译通过

在调用sendClearMsg这个点上,编译器直到base class是个template特化版本,且它直到这个特化版不提供sendClearMsg函数。

总结一下,本条款讨论的是,面对指涉base class member之无效references,编译器的诊断时间可能发生在早起(当解析derived class template定义时),也可能发生在晚期(当template被特定值template实参具体化时)。C++宁愿较早诊断,这也是为什么当base classes从template具体化时,它假设对那些base classes的内容毫无所悉的缘故。

总结

  • 可在derived class templates内通过this->指涉base class templates内的成员名称,或藉由一个明白写出base class资格修饰符完成。
STM32F103C8T6+HX711实战:从零打造智能体重秤(附OLED显示与语音播报) 本文详细介绍了基于STM32F103C8T6微控制器和HX711称重模块,从零构建智能体重秤的完整实战教程。内容涵盖硬件选型、系统架构、驱动编写、数据滤波校准算法,并实现了OLED显示与语音播报功能。该项目是嵌入式学习的综合性实践,尤其适合作为毕业设计课题,帮助开发者掌握传感器应用、数据处理及人机交互等核心技能。 阅读详情

相关推荐

SAP Script表单开发中ABAP preform方法的实战应用与调试技巧

本文深入探讨了在SAP Script表单开发中调用ABAP程序(preform方法)的实战应用与调试技巧。通过一个获取供应商版本名称的实例,详细讲解了如何在SE71中嵌入PERFORM指令、编写ABAP子程序进行数据查询与处理,并重点介绍了使用SAP Script Debugger进行高效调试的方法,帮助开发者灵活扩展表单功能,实现复杂业务逻辑。

o0p1q2r3的博客 552

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

## 一. 内容 1. 我们从一个例子入手,假设我们要设计游戏中人物的相关列表,比如 buff 列表,物品列表等等,一个显而易见的设计是: ```cpp class Buff { public: virtual ~Buff() = default; virtual void Start() =0; virtual void End() =0; virtual void OnTick() =0; }; class RedBuff : public Buf

MoOtA 320

Cursor+Playwright MCP实现UI全自动化,告别繁琐元素定位!

摘要:本文探讨了利用Cursor和PlaywrightMCP实现UI自动化测试的创新方法。通过将AI编程助手与浏览器自动化技术结合,解决了传统UI自动化测试中元素定位繁琐、维护成本高等痛点。文章详细介绍了项目架构设计、AI使用技巧(包括精准提问和编写.mdc规则文档),以及如何实现自然语言测试用例到自动化脚本的转换。最终实现了测试人员只需输入手工测试步骤,AI即可自动生成完整测试代码的智能化流程,并展望了通过Excel表格一键生成测试用例的未来发展方向,为UI自动化测试提供了高效解决方案。

baidu_29774881的博客 2795

《Effective C++》学习笔记(条款43:学习处理模板化基类内的名称)

最近开始看《Effective C++》,为了方便以后回顾,特意做了笔记。若本人对书中的知识点理解有误的话,望请指正!!! 假设我们有一个程序,它能够传送信息到若干不同的公司去。信息要么被加密,要么就是未加密。如果在编译期间我们拥有足够的信息来决定哪一个信息传至哪家公司,就可以采用基于 template 的方法: class CompanyA { public: ... void sendCleartext(const std::string& msg); void send

ZLX的博客 820

FA 新增资产API Demo

DECLARE l_trans_rec fa_api_types.trans_rec_type; l_dist_trans_rec fa_api_types.trans_rec_type; l_asset_hdr_rec fa_api_types.asset_hdr_rec_type; l_asset_desc_rec fa_a

chosen1brain的专栏 2142

CST 仿真_NFC仿真

在CST中实现NFC天线单体的仿真

qq_43706810的博客 3961

《Effective C++》学习笔记——条款43

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

继续激情,继续奋斗。 828

STRING数据库的蛋白质相互作用(PPI)网络分析

在转录调控相关的文献中,我们经常能够看到这样的蛋白质相互作用网络(protein protein interaction network,PPI network)。具体而言,这些相关的文献中首先通过RNA-seq、表达谱芯片或者蛋白质组分析等,找到了在不同分组样本间一系列的差异表达基因或蛋白。

weixin_43029125的博客 9458

条款43:学习处理模板化基类内的名称(调用模板基类内函数的方法)

1、存在的问题: template<typename T1> class Class_A{ public: void send1(T1 var); }; template <typename T1> class Class_B:public Class_A< T1 >{ public: void send2(T1 var) { send1(var); } ...

个人工作中学到的专业技术与职业发展知识笔记 453

Allegro快速编辑丝印文字操作指导

选择setup-Application Mode-placement edit。Allegro支持丝印文字的编辑,下面介绍快速编辑丝印文字的两种方法如下。这个功能还支持拷贝粘贴,可以从其它地方拷贝过来,免去了书写的麻烦。出现text edit窗口,输入自己需要的文字,点击ok。鼠标移动到丝印文字位置,文字会被高亮起来。Allegro快速编辑丝印文字操作指导。点击丝印文字,丝印会被高亮起来。选择Text edit 命令。输入需要更改后的文字,如下。右击选择text edit。以编辑下方丝印文字为例。

weixin_54787054的博客 4193

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

effective c++条款43 学习处理模板化基类内的名称 首先为什么要处理模板化基类的名称呢? 举个例子 我们现在要设计一个程序,要传送信息到若干不同的公司去,信息要不译成密码,要不就是未加工的文字。我们在编译期间有足够的信息来决定哪一个信息传至哪一家公司。我们实现的方式有两种 一种是动态绑定,就是在使用运行期多态,定义子类重写两个函数,另一种就是使用静态多态。(编译期多态和运行期多态的差别,编译器多态是决定那个重载函数被调用的,也就是说编译器多态是一种静态多态,而运行期多态是一种动态绑定) log是

weixin_46388214的博客 189

最大面积验证码通用解法

最大面积验证码、面积验证通用解法

九月镇灵将的博客 1044

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

Effective_C++_43

君鼎的博客 644

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

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

1. 在继承模板基类时,C++拒绝在模板化基类(templatized base classes)内寻找继承而来的名称,例如,对于以下模板基类: template<typename T> class Base{ public: void fun(){ ... } ... private: ... } ...

ddzai7557的博客 138

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

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

考虑以下类: template<typename Company> class MsgSender { public: ... void sendClear(...); }; template<typename Company> class LoggingMsgSender : public MsgSender<Company> { public: .....

qq_44800780的博客 251

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

Effective C++ 学习笔记 条款43 学习处理模板化基类内的名称

这是好的设计,因为它绕开了隐藏继承名称的问题(参见条款33),也避免了重新定义继承来的非虚函数所固有的问题(参见条款36)。我们需要理解为什么。这被称为全模板特化(total template specialization),即模板 MsgSender 针对类型 CompanyZ 进行了特化,而且这个特化是全的——一旦类型参数被定义为 CompanyZ,模板的其他参数就不可能再有任何变化了。从名称可见性的角度来看,这三种做法效果相同:它们都向编译器承诺,基类模板的任何后续特化都会支持通用模板所提供的接口。

tus00000的博客 237

Effective C++笔记_条款43 学习处理模板化基类内的名称

Effective C++笔记_条款43 学习处理模板化基类内的名称      开篇就来了一个示例代码,整个这个小节就围绕这个示例代码来描述模板化基类内的名称(函数)。主要是因为C++知道base class templates有可能被特化,而那个特化版本肯呢个不提供和一般性template相同的接口。因此它往往拒绝在templatized base classes(模板化基类)

zdy0_2004的专栏 516

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

(一) 注意从 “面向对象的C++” 转向 “模板C++” 时继承可能遭遇问题 :由于基类模板可能被特化,而该特化版本可能会改变成员,因此C++拒绝在模板化基类中寻找继承而来的名称。 (二) 看下面的例子: 假设将信息传送到不同的公司去,传送方式包括明文传送和密文传送,采用模板类的设计方法: class CompanyA { public: ... void sendCl

u010470972的专栏 1002

Effective C++条款43:模板与泛型编程之(学习处理模板化基类内的名称)

待续

董哥的黑板报 845

Effective C++条款43:模板与泛型编程——学习处理模板化基类内的名称

一、模板中,派生类不可调用模板基类的成员函数 在模板类中,如果一个派生类在其方法中调用了基类的方法,那么这段代码可能无法编译通过 备注(重点): 这一现象是与编译器有关的,Effective C++的作者编译的时候出错 本人使用VS编译的时候没有出错 因此这个问题是与编译器有关 演示说明 假设现在有这样一个类体系: 我们有若干公司类,其包含两个成员函数可以用来将信息发送到公司(一个为发送加密信息,一个为发送不加密信息) 有一个MsgSender类,其中有两个成员函数,函数中可以

www_dong的博客 207

《Effective C++》读书笔记 条款43:学习处理模板化基类的名称

假设我们需要发送信息到若干个不同的公司,每个公司发送的信息分为加密和不加密的两种类型。可以利用模板技术写发送信息的类 按照书上的例子,比如现有A、B两个公司,都支持发送普通消息和加密消息。MsgSender是一个模板类,在编译器根据要给哪个公司发送消息调用相应公司的函数。这个做法是可行的。 class CompanyA { public: void SendCleartext(const ...

junya_zhang的博客 192

Effective C++学习笔记_条款43:学习处理模板化基类内的名称

有两个不同的公司,想发送不同的消息给这个两个公司,消息可以是明文,也可以是译成密码的形式发送。于是我们采用动态绑定。先定义一个发送消息的类BaseSendMsg,包含SendMsgTxt以及SendMsgSecret两个virtual函数,然后另外定义两个类CompanyA和CompanyB,这两个类分别继承BaseSendMsg,并分别重写自己的发送消息的函数。   但是我们这里想换一种思路,使

zjwson的博客 412
上一篇: 《Effective C++》:条款41-条款42
下一篇: 《Effective C++》:条款44-条款45
KangRoger
博客等级 码龄14年 728粉丝 280原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值