跟我学c++中级篇——模板的继承

一、继承

面向对象编程有三个特点:封装、继承和多态。其中继承在其中起着承上启下的作用。一般来说,继承现在和组合的应用比较难区分,出于各种场景和目的,往往各有千秋。但目前主流的观点,一般是如果没有特殊情况,比如需要处理数据的访问权限、重新实现功能(多态),都是推荐使用组合的。继承一个是复杂另外一个不灵活。人类更擅长的是组件拼装,而不是重新造组件。同时,组合更适合开发设计原则,比如开闭原则、单一职责等等,好像只有一个里氏替换不合适。
但是在有些时候儿,继承还是必须要用的,那么今天重点谈一下,在模板中如何使用继承。

二、模板继承及方式

模板的继承一如普通类的继承,形式是基本相同的,可能有些实现上看上去比较复杂。在模板开发的继承中一般有四种形式的继承方式(以父子类来描述继承,不指明的为普通类)即:
1、父类为模板
类似于:

template <typename T> 
class Base {
};
class Derived:public Base<int>{
};

这里面有一个前文提到的CRTP模式:

template <class T>
class Base
{// 基类方法可以通过模板继承的方式来访问继承类的成员
};
class Derived : public Base<Derived>
{
};

2、子类为模板

class Base {
};
template <typename T> 
class Derived:public Base{
};

3、父子类均为模板
此种情况又有两种形式:

//第一种实例化父类模板参数
template <typename T>
class Base {
};
template <typename T> 
class Derived:public Base<int>{
};
//第二种非实例化父类模板参数
class Base {
};
template <typename T> 
class Derived:public Base<T>{
};

4、父类的模板参数被继承

template <typename T> 
class Base {
};
class Derived:public T{
};

此处没有谈多继承,这个在编程时尽量避免。有兴趣可以自己搞一下。在父子类继承中,如果父类是模板,会出现在子类中访问父类变量无法直接访问的现象。需要使用父类的类名限制( Base ::basemember_;)或者直接在变量前加this进行访问。
原因是:继承的模板要进行两次编译,每一次只处理和本身的数据相关,也就是说只管自己的地盘,什么 父类模板参数啥的都暂时忽略;第二步,再处理上面没有处理的模板参数部分。所以此时直接访问父类继承过来的变量和函数会找不到报错,重要的要使用某种方式把这部分延期到第二步编译,那么就没有什么 问题了。方法就是上面的两种方式,这样编译器就明白这些不是本身模板的内容就放到第二步处理。
另外一个选择继承这里也没有说明,什么 意思呢?就是模板参数有N个,但父类模板只用其中的<N个,比如有子类有三个模板参数,父类只用了其中两个或者一个,其它的为子类自用。这种其实上面的组合方式,不再赘述,但是下面会给出例子。

三、例程:

下面看一个例程:

#include <iostream>
namespace TT {//========================父类为模板=========================================
template <typename T> class BaseT {
public:BaseT() = default;BaseT(int a) : d_(a) {std::cout << "call template Parent class BaseT" << std::endl;}~BaseT() {}protected:int d_ = 0;
};
class Derived : public BaseT<int> {
public:// Derived() = default;Derived(int a) : BaseT<int>(a) {std::cout << "call derived sub class no template!" << std::endl;std::cout << " Parent class Base d_:" << d_ << std::endl;}Derived() : BaseT<int>(10) {std::cout << "call derived sub class no template 10!" << std::endl;std::cout << " Parent class Base d_:" << d_ << std::endl;}~Derived() {}
};
//=========================子类为模板========================================
class Base {
public:Base() = default;Base(int a) : d_(a) { std::cout << "call  Parent class Base" << std::endl; }~Base() {}protected:int d_ = 0;
};
template <typename T> class DerivedT : public Base {
public:DerivedT(){};DerivedT(int a) : Base(a) {std::cout << "sub template class call Parent class Base" << std::endl;std::cout << " Parent class Base d_:" << d_ << std::endl;}~DerivedT() {}
};
//=======================父子均为模板但父类特化===============================
template <typename TT> class DerivedTTa : public BaseT<int> {
public:DerivedTTa() {std::cout << "sub a template class call init Parent template class BaseT"<< std::endl;}DerivedTTa(int a) : BaseT(a), d_(a) {std::cout << "sub a template class call init Parent template class BaseT"<< std::endl;std::cout << " Parent class Base d_:" << d_ << std::endl;}~DerivedTTa() {}protected:TT d_ = 0;
};
//=====================父子均为模板但父类未特化===============================
template <typename TT> class DerivedTTb : public BaseT<TT> {
public:DerivedTTb() {}DerivedTTb(TT a) : BaseT<TT>(a) {std::cout << "sub b template class call Parent template class BaseT"<< std::endl;std::cout << " Parent class BaseT d_:" << this->d_ << " " << BaseT<TT>::d_<< std::endl;}~DerivedTTb() {}
};
//=====================继承模板参数========================================
template <typename T> class DerivedP : public T {
public:DerivedP() {std::cout << "template class inherit template class Parameter" << std::endl;}DerivedP(int a) : T(a) {std::cout << "template class inherit template class Parameter" << std::endl;std::cout << "parameter a is:" << a << std::endl;}~DerivedP() {}
};
//====================选择继承============================================
template <typename T, typename N, typename P>
class DerivedPM : public BaseT<N> {
public:DerivedPM() {std::cout << "template class call template Mult class Parameter"<< std::endl;};DerivedPM(T t, N n, P p) : BaseT<N>(n), t_(t), n_(n), p_(p) {std::cout << "template class call template Mult class Parameter"<< std::endl;std::cout << "parameter t,n,p is:" << t << " " << n << " " << p<< std::endl;std::cout << "parameter d_ is:" << this->d_ << std::endl;}protected:T t_;N n_;P p_;
};
} int main() {TT::Derived td;TT::Derived td1(10);TT::Derived dd;TT::DerivedT<int> tdt;TT::DerivedT<int> tdt1(10);TT::DerivedTTa<int> tdta;TT::DerivedTTa<int> tdta1(10);TT::DerivedTTb<int> tdtb;TT::DerivedTTb<int> tdtb1(10);TT::DerivedP<TT::Base> tdp;TT::DerivedP<TT::BaseT<TT::Base>> tdpbb;TT::DerivedPM<int, int, int> tddpm;TT::DerivedPM<int, int, int> tddpm1(1, 2, 3);return 0;
}

例程不难,关键看模板继承模板中那个父类d_如何访问,有的为什么要加this(或Base:😃,有的为什么不加,模板一但特化,便和普通类一致,不需要this。仔细想一下,就会越来越明白。那个CRTP的在前文有专门的分析说明,此处不再举例。有兴趣可以 翻一下前面的例程

四、总结

模板不管用得多少 ,总会遇到一些很古怪的错误,但这些错误其实是编译器不友好,或者说模板比较复杂导致编译无法特别准确的提供一些信息造成的。所以重点还是要把模板的基础知识学清楚,一点点的推导出来,再配合相关的错误信息,就可以较快的解决问题。
网上还有先写非模板代码然后 再用模板代码改写的。这样也是一个不错的方法,虽然有点费事儿,但只要用在复杂的情况下就好了。重点在于不断的反复的把模板的知识对照着代码编写,熟悉后自然就渐渐明白道理,再写就逐渐顺手多了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/13622.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里Java开发手册~集合处理

1. 【强制】关于 hashCode 和 equals 的处理&#xff0c;遵循如下规则&#xff1a; 1 &#xff09; 只要重写 equals &#xff0c;就必须重写 hashCode 。 2 &#xff09; 因为 Set 存储的是不重复的对象&#xff0c;依据 hashCode 和 equals 进行判断&#xff…

【雕爷学编程】MicroPython动手做(02)——尝试搭建K210开发板的IDE环境3

4、下载MaixPy IDE&#xff0c;MaixPy 使用Micropython 脚本语法&#xff0c;所以不像 C语言 一样需要编译&#xff0c;要使用MaixPy IDE , 开发板固件必须是V0.3.1 版本以上&#xff08;这里使用V0.5.0&#xff09;, 否则MaixPy IDE上会连接不上&#xff0c; 使用前尽量检查固…

libhv之hio_t分析

上一篇文章解析了fd是怎么与io模型关联。其中最主要的角色扮演者&#xff1a;hio_t 1. hio_t与hloop的关系 fd的值即hio_t所在loop ios变量中所在的index值。 hio_t ios[fd] struct hloop_s { ...// ios: with fd as array.index//io_array保存了和hloop关联的所有hio_t&…

基于fpga_EP4CE6F17C8实现的呼吸灯

文章目录 前言实验手册&#xff08;EP4CE6F17C8&#xff09;一、实验目的二、实验原理理论原理 三、系统架构设计四、模块说明1&#xff0e;模块端口信号列表2&#xff0e;状态转移图3&#xff0e;时序图 五、仿真波形图六、引脚分配七、代码实现八、仿真代码九、板级验证效果 …

PyTorch高级教程:自定义模型、数据加载及设备间数据移动

在深入理解了PyTorch的核心组件之后&#xff0c;我们将进一步学习一些高级主题&#xff0c;包括如何自定义模型、加载自定义数据集&#xff0c;以及如何在设备&#xff08;例如CPU和GPU&#xff09;之间移动数据。 一、自定义模型 虽然PyTorch提供了许多预构建的模型层&#…

在服务器上部署gradio demo,外网可以通过服务器ip访问

因为业务需要提供一个可持续性访问的链接&#xff0c;所以gradio提供的临时链接&#xff08;shareTrue&#xff09;不能用。 用下面的代码即可部署到服务器IP。 import gradio as gr import timedef test(x):time.sleep(4)return xiface gr.Interface(test, "textbox&qu…

深入浅出索引(上)

场景引入 某一个 SQL 查询比较慢&#xff0c;分析完原因之后&#xff0c;你可能就会说“给某个字段加个索引吧”之类的解决方案。但到底什么是索引&#xff0c;索引又是如何工作的呢&#xff1f; 一句话简单来说&#xff0c;索引的出现其实就是为了提高数据查询的效率&#xff…

Golang Devops项目开发(1)

1.1 GO语言基础 1 初识Go语言 1.1.1 开发环境搭建 参考文档&#xff1a;《Windows Go语言环境搭建》 1.2.1 Go语言特性-垃圾回收 a. 内存自动回收&#xff0c;再也不需要开发人员管理内存 b. 开发人员专注业务实现&#xff0c;降低了心智负担 c. 只需要new分配内存&#xff0c;…

Mysql定时删除表数据

由于用户环境有张日志表每天程序都在狂插数据&#xff0c;导致不到一个月时间&#xff0c;这张日志表就高达200多万条记录&#xff0c;但是日志刷新较快&#xff0c;里面很多日志没什么作用&#xff0c;就写了个定时器&#xff0c;定期删除这张表的数据。 首先查看mysql是否开启…

L---泰拉瑞亚---2023河南萌新联赛第(三)场:郑州大学

链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 示例1 输入 1 10 3 5 输出 3 说明 只有一把回旋镖&#xff0c;你可以先打两次伤害为3的&#xff0c;再打一次倾尽全力的&#xff0c;造成的伤害为5。总伤害为33511&#xff0c;即可获得胜…

FPGA设计时序分析三、恢复/去除时间

目录 一、背景说明 二、工程设计 2.1 工程代码 2.2 综合结果 一、背景说明 ​恢复时间recovery和去除时间removal和setup、holdup类型&#xff0c;不同点是数据信号为控制信号&#xff0c;如复位&#xff0c;清零&#xff0c;使能信号&#xff0c;更多的是异步的复位信号&a…

Spring Cloud Eureka 服务注册和服务发现超详细(附加--源码实现案例--及实现逻辑图)

文章目录 EurekaEureka组件可以实现哪些功能什么是CAP原则&#xff1f;服务注册代码实战搭建注册中心服务A搭建服务B搭建启动服务启动注册中心启动服务A启动服务B 结束语 Eureka 这篇文章先讲述一下Eureka的应用场景、代码实现案例&#xff0c;多个服务模块注册到Euraka中&…

使用MyBatis(2)

目录 一、定义接口、实体类、创建XML文件实现接口&#xff09; 二、MyBatis的增删改查 &#x1f345;1、MyBatis传递参数查询 &#x1f388;写法一 &#x1f388;写法二 &#x1f388;两种方式的区别 &#x1f345;2、删除操作 &#x1f345;3、根据id修改用户名 &#x…

【C++】做一个飞机空战小游戏(一)——使用getch()函数获得键盘码值

[导读]本系列博文内容链接如下&#xff1a; 【C】做一个飞机空战小游戏(一)——使用getch()函数获得键盘码值 【C】做一个飞机空战小游戏(二)——利用getch()函数实现键盘控制单个字符移动 最近想用c做一个小游戏&#xff0c;游戏的主要内容是利用键盘控制一个飞机躲避和击落屏…

Winform制作的用户界面在高DPI下缩放问题

引言 熟悉Winform的小伙伴应该都遇到过 在100%缩放下制作的用户界面在其他缩放百分比下会出现字体超出边框的情况&#xff0c;导致用户体验大打折扣。用户程序DPI感知是默认打开的&#xff0c;此时可以通过关闭这种感知来禁用字体的缩放&#xff0c;在这种情况下&#xff0c;用…

【前端知识】JavaScript——设计模式(工厂模式、构造函数模式、原型模式)

【前端知识】JavaScript——设计模式&#xff08;工厂模式、构造函数模式、原型模式&#xff09; 一、工厂模式 工厂模式是一种众所周知的设计模式&#xff0c;广泛应用于软件工程领域&#xff0c;用于抽象创建特定对象的过程。 优点&#xff1a;可以解决创建多个类似对象的…

通俗易懂,十分钟读懂DES,详解DES加密算法原理,DES攻击手段以及3DES原理。Python DES实现源码

文章目录 1、什么是DES2、DES的基本概念3、DES的加密流程4、DES算法步骤详解4.1 初始置换(Initial Permutation&#xff0c;IP置换)4.2 加密轮次4.3 F轮函数4.3.1 拓展R到48位4.3.2 子密钥K的生成4.3.3 当前轮次的子密钥与拓展的48位R进行异或运算4.3.4 S盒替换&#xff08;Sub…

mysql的主从复制

1.主从复制的原理 主从复制的原理是通过基于日志的复制方式实现数据的同步。当主服务器上发生数据变更时&#xff0c;会将这些变更写入二进制日志&#xff08;Binary Log&#xff09;中。从服务器通过连接到主服务器&#xff0c;请求从主服务器获取二进制日志&#xff0c;并将…

QPainter绘制雷达界面

文章目录 功能实现定义的结构体定义的函数效果图gitee源码链接 功能实现 相较于上一版&#xff0c;这一版添加的功能有&#xff1a; 1、自适应窗口 2、扫描方式&#xff08;圆周扫描、扇形扫描&#xff08;指定起始角度和结束角度&#xff09;&#xff09; 3、扫描方向&#x…

Linux:ELK:日志分析系统(使用elasticsearch集群)

原理 1. 将日志进行集中化管理&#xff08;beats&#xff09; 2. 将日志格式化&#xff08;logstash&#xff09; 将其安装在那个上面就对那个进行监控 3. 对格式化后的数据进行索引和存储&#xff08;elasticsearch&#xff09; 4. 前端数据的展示&#xff08;kibana&…