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

跟我学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的在前文有专门的分析说明,此处不再举例。有兴趣可以 翻一下前面的例程

四、总结

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

相关文章:

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

一、继承 面向对象编程有三个特点&#xff1a;封装、继承和多态。其中继承在其中起着承上启下的作用。一般来说&#xff0c;继承现在和组合的应用比较难区分&#xff0c;出于各种场景和目的&#xff0c;往往各有千秋。但目前主流的观点&#xff0c;一般是如果没有特殊情况&…...

需求分析案例:消息配置中心

本文介绍了一个很常见的消息推送需求&#xff0c;在系统需要短信、微信、邮件之类的消息推送时&#xff0c;边界如何划分和如何设计技术方案。 1、需求 一个系统&#xff0c;一般会区分多个业务模块&#xff0c;并拆分成不同的业务系统&#xff0c;例如一个商城的架构如下&am…...

自动化测试——环境

一、搭建环境 1、安装Slenium pip install selenium 2、安装浏览器驱动-》查询浏览器版本-》下载对应版本驱动-》在path路径中配置&#xff08;浏览器更新需要重新下载&#xff09; pip install webdriver -helper&#xff08;自动化&#xff09;python3.9以上 pip install 安…...

短视频矩阵营销系统技术开发者开发笔记分享

一、开发短视频seo抖音矩阵系统需要遵循以下步骤&#xff1a; 1. 确定系统需求&#xff1a;根据客户的需求&#xff0c;确定系统的功能和特点&#xff0c;例如用户注册登录、视频上传、视频浏览、评论点赞等。 2. 设计系统架构&#xff1a;根据系统需求&#xff0c;设计系统的…...

vue2和vue3引用ueditor的区别

官方文档入口 UEditor Docs vue2使用方式 UE.vue组件 <template><div><script id"editor" type"text/plain"></script><Upload v-if"isupload" :config"{total:9}" :isupload"isupload" ret…...

【每日运维】RockyLinux8非容器化安装Mysql、Redis、RabitMQ单机环境

系统版本&#xff1a;RockyLinux 8.6 安装方式&#xff1a;非容器化单机部署 安装版本&#xff1a;mysql 8.0.32 redis 6.2.11 rabbitmq 3.11.11 elasticsearch 6.7.1 前置条件&#xff1a;时间同步、关闭selinux、主机名、主机解析host 环境说明&#xff1a;PC电脑VMware Work…...

第一次后端复习整理(JVM、Redis、反射)

1. JVM 文章仅为自身笔记 详情查看一篇文章掌握整个JVM&#xff0c;JVM超详细解析&#xff01;&#xff01;&#xff01; 1.1 什么是JVM jvm是Java虚拟机 1.2 Java文件的编译过程 程序员编写代码形成.java文件经过javac编译成.class文件再通过JVM的类加载器进入运行时数据…...

python的web学习(一)-初识django

文章目录 软件创建项目默认项目文件说明App的概念(应用)apps.py编写URL和视图函数对应关系【urls.py】编写视图函数【views.py】启动服务 软件 python下载 django下载 创建项目 django-admin startproject 文件名默认项目文件说明 项目名 manage.py(项目管理&#xff0c;启…...

JavaWeb+jsp+Tomcat的叮当书城项目

点击以下链接获取源码&#xff1a; https://download.csdn.net/download/qq_64505944/88123111?spm1001.2014.3001.5503 技术&#xff1a;ssm jsp JDK1.8 MySQL5.7 Tomcat8.3 源码数据库课程设计 功能&#xff1a;管理员与普通用户和超级管理员三个角色&#xff0c;管理员可…...

【嵌入式Linux系统开发】——系统移植概述

目录 &#x1f349;&#x1f349;一、什么是嵌入式系统 &#x1f349;&#x1f349;二、嵌入式系统操作 &#x1f349;&#x1f349;三、嵌入式Linux的特点 &#x1f349;&#x1f349;四、嵌入式系统的组成 1、硬件和软件 2、硬件层 3、中间层 4、软件层 5、 功能层与执…...

升讯威在线客服系统是如何实现对 IE8 完全完美支持的(怎样从 WebSocket 降级到 Http)【干货】

简介 升讯威在线客服与营销系统是基于 .net core / WPF 开发的一款在线客服软件&#xff0c;宗旨是&#xff1a; 开放、开源、共享。努力打造 .net 社区的一款优秀开源产品。 完整私有化包下载地址 &#x1f4be; https://kf.shengxunwei.com/freesite.zip 当前版本信息 发布…...

用VMware给运行在VMware上的CentOS7生成一个以SSH方式连接VMware上的CentOS7的运行在Windows上的命令行窗口

2023年7月27日&#xff0c;周四早上 目录 一个发现生成方法如果上面的方法连接失败&#xff0c;就采取这个方法 一个发现 今天早上无意间发现VMware可以生成一个以SSH方式连接着CentOS7的Windows命令行窗口&#xff0c; 这样做可以带来一定的便利性 &#xff1a; 方便复制、…...

C语言基础-3

1、函数 函数是C语言代码的基本组成部分&#xff0c;它是一个小的模块&#xff0c;整个程序由很多个功能独立的模块&#xff08;函数&#xff09;组成。这就是程序设计的基本分化方法。 main&#xff1a;C语言中所谓的主函数&#xff0c;主函数就是一种特别的函数。特别之处在于…...

Python 编程规范进阶(1) | 命名规范

养成良好的开发、编程习惯 跟着google开源项目走 https://github.com/google/styleguide 近期Target&#xff1a; 命名规范&#xff1b; Pythonic 积累 按照需求写需要的API&#xff1b; 写前先动脑子&#xff0c;比如画流程图&#xff0c;测试接口&#xff1b; Google 推荐的P…...

算法----二叉搜索树中第K小的元素

题目 二叉搜索树中第K小的元素 给定一个二叉搜索树的根节点 root &#xff0c;和一个整数 k &#xff0c;请你设计一个算法查找其中第 k 个最小元素&#xff08;从 1 开始计数&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,1,4,null,2], k 1 输出&#xff…...

阿里Java开发手册~安全规约

1. 【强制】隶属于用户个人的页面或者功能必须进行权限控制校验。 说明&#xff1a; 防止没有做水平权限校验就可随意访问、修改、删除别人的数据&#xff0c;比如查看他人的私信 内容、修改他人的订单。 2. 【强制】用户敏感数据禁止直接展示&#xff0c;必须对展示数据进…...

消息中间件RabbitMQ——学习笔记

❤ 作者主页&#xff1a;欢迎来到我的技术博客&#x1f60e; ❀ 个人介绍&#xff1a;大家好&#xff0c;本人热衷于Java后端开发&#xff0c;欢迎来交流学习哦&#xff01;(&#xffe3;▽&#xffe3;)~* &#x1f34a; 如果文章对您有帮助&#xff0c;记得关注、点赞、收藏、…...

爬虫005_python类型转换_其他类型转换为整型_转换为Float类型_转换为字符串_转换为布尔值---python工作笔记023

首先来看,字符串转换成int 很简单 float转换成int 会把小数点后面的内容丢掉 boolean转换为int true是1 false 是0 然后字符串转换为int,要注意 不能有特殊字符比如1.23 中有点 就报错 上面字符串12ab,有ab也报错 看上面...

SpringBoot复习:(5)使用PropertySource注解

一、自定义的一个配置文件 age33 nameliu二、实体类 package com.example.demo.domain;public class Student {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name name;}public int getAge() {retur…...

webrtc 支持H265(三) 总结

文章目录 web端的解码及渲染的实现应用场景单向视频流的场景datachannel通道的稳定性解码性能 双向视频流的场景有音频流的场景 web端的解码及渲染的实现 在前面的文章中介绍了ZLMediaKit的修改方法&#xff0c;在web端的播放器可以参照这个实现&#xff0c;基于wasm H265播放…...

Windows使用Notepad++编辑Linux服务器的文件

&#x1f680; Windows使用Notepad编辑Linux服务器的文件 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介…...

升级你的数据采集引擎 使用多线程与代理池提升HTTP代理爬虫性能

在信息爆炸的时代&#xff0c;海量数据的采集和分析成为了企业发展和决策的关键。本文将分享如何通过多线程和代理池的应用&#xff0c;助您升级数据采集引擎&#xff0c;提高数据获取效率和稳定性。 HTTP代理爬虫作为数据采集的重要工具&#xff0c;其性能直接影响着数据采集…...

flask实现一个登录界面

flask实现一个登录界面 基础的Flask项目结构 forms.py&#xff1a;定义登录表单和表单字段的文件。templates/login.html&#xff1a;用于渲染登录表单的 HTML 模板文件。routes.py&#xff1a;定义应用的路由和视图函数的文件。__init__.py&#xff1a;创建并初始化 Flask 应…...

redis的四种模式优缺点

redis简介 Redis是一个完全开源的内存数据结构存储工具&#xff0c;它支持多种数据结构&#xff0c;以及多种功能。Redis还提供了持久化功能&#xff0c;可以将数据存储到磁盘上&#xff0c;以便在重启后恢复数据。由于其高性能、可靠性和灵活性&#xff0c;Redis被广泛应用于…...

maven本地仓库地址修改+maven国内镜像设置+maven运行所需pos.xml文件配置基本写法

1&#xff0c;maven本地仓库地址修改 maven在使用过程中&#xff0c;本地项目仓库其空间占用会越来越大&#xff0c;但是其默认仓库位置往往是以C盘为主&#xff0c;C盘作为系统盘常常会遇到所在盘空间占满的情况&#xff0c;所以我们将其改至其他硬盘空间位置为适合做法&#…...

Jenkins集成SonarQube保姆级教程

Jenkins是自动化部署平台&#xff0c;一个粗眉大眼的糙汉子&#xff01; SonarQube是代码扫描平台&#xff0c;一个眉目清秀的小女子&#xff01; 有一天&#xff0c;上天交给我一个任务&#xff0c;去撮合撮合他们&#xff01; 我抬头看了看天&#xff0c; 不&#xff0c;…...

Git的安装以及本地仓库的创建和配置

文章目录 1.Git简介2.安装Git2.1在Centos上安装git2.2 在ubuntu上安装git 3.创建本地仓库4.配置本地仓库 1.Git简介 Git是一个分布式版本控制系统&#xff0c;用于跟踪和管理文件的更改。它可以记录和存储代码的所有历史版本&#xff0c;并可以方便地进行分支管理、合并代码和协…...

现在运动耳机什么牌子的好用、最好的运动耳机推荐

对于注重身体健康的小伙伴来说&#xff0c;每周必然都少不了有规律的运动&#xff0c;而运动的时候耳边没有音乐的陪伴总是稍显枯燥无味&#xff0c;很难让人提起干劲来。有些小伙伴觉得运动的时候戴着耳机&#xff0c;稍微跳动几下耳机就开始松动&#xff0c;随时都要分心提防…...

监控指标与监控类型

监控体系中最基础的是监控指标&#xff0c;监控系统就是围绕指标的采集、传输、存储、分析、可视化的一个系统。 监控指标是指数值类型的监控数据&#xff0c;比如某个机器的内存利用率&#xff0c;某个 MySQL 实例的当前连接数&#xff0c;某个 Redis 的最大内存上限等等。不…...

Vue实现柱状图横向自动滚动

Vue实现柱状图横向自动滚动 1. 前言2. 代码3、实现效果图 1. 前言 原理&#xff1a;通过定时器修改Echarts的配置&#xff08;options&#xff09;达到我们想要的效果。 此外&#xff0c;我们还需要了解Echarts中dataZoom这个组件&#xff0c;这个组件用于&#xff1a;用于区域…...