浅谈C++|类的继承篇

 

引子: 

继承是面向对象三大特性之一、有些类与类之间存在特殊的关系,例如下图中:



我们发现,定义这些类时,下级别的成员除了拥有上一级的共性,还有自己的特性。
这个时候我们就可以考虑利用继承的技术,减少重复代码。

一.继承基本语法 

语法:class 子类:继承方式 父类 

优点:减少重复代码

子类也叫派生类,父类也叫基类

代码: 

#include <iostream>
using namespace std;
class person {   //定义person基类
public:int age;int height;int weight;string name;void show() {cout << "age=" << age << endl;cout << "height=" << height << endl;cout << "weight=" << weight << endl;cout << "name=" << name << endl;}
};class women :public person {
public:string xingbie;void shou_x() {cout << "xingbie=" << xingbie<<endl;}
};void fun() {women p;p.age = 18;p.height = 180;p.name = "tom";p.weight = 160;p.xingbie = "女";p.show();p.shou_x();
}
int main() {fun();return 0;
}

 派生类中的成员,包含两大部分:
1.类是从基类继承过来的,-类是自己增加的成员。
2.从基类继承过过来的表现其共性,而新增的成员体现了其个性。

 二.继承方式

 继承方式:

public:     公有继承

protected:保护继承

private:    私有继承

代码:

#include <iostream>
using namespace std;
class father {
public:int A;
protected:int B;
private:int C;
};class son1 :public father {    //公有继承
public:void fun() {cout << "A=" << A << endl;  //公有变为公有cout << "B=" << B << endl;  //保护变为保护//cout << "C=" << C << endl;//私有不可访问}
};class son2 :protected father {    //保护继承
public:void fun() {cout << "A=" << A << endl;  //公有变为保护cout << "B=" << B << endl;  //保护变为保护//cout << "C=" << C << endl;//私有不可访问}
};class son3 :private father {    //私有继承
public:void fun() {cout << "A=" << A << endl;  //公有变为私有cout << "B=" << B << endl;  //保护变为私有//cout << "C=" << C << endl;//私有不可访问}
};void fun() {son1 p1;son2 p2;son3 p3;p1.A = 10;p1.fun();
}
int main() {fun();return 0;
}

 私有成员全不见,公不变,保全保,私全私。

 三.继承中的对象模型

继承中,基类私有变量虽然访问不到,但是已经被继承,只是被隐藏了。

 代码:

#include <iostream>
using namespace std;
class father {
public:int A;
protected:int B;
private:int C;
};
class son :private father {
public:int age;
};
int main() {cout << sizeof(son) << endl;return 0;
}

四.继承构造和析构顺序

子类继承父类时,会先创建一个父类。析构的顺序和构造顺序正好相反。

 代码:

#include <iostream>
using namespace std;
class father {
public:father() {cout << "father的构造哈数" << endl;}~father() {cout << "father的析构函数" << endl;}
};
class son :public father {
public:son() {cout << "son的构造函数" << endl;}~son() {cout << "son的析构函数" << endl;}
};
void fun() {son a;
}
int main() {fun();return 0;
}

五.继承同名成员处理方式

当基类中的成员变量以及成员函数,和派生类的成员变量和函数重名时,基类默认调用派生类的成员函数和成员变量。要想调用基类的成员函数和成员变量,需要加上基类的作用域;

代码:

#include <iostream>
using namespace std;
class father {
public:int A;father() {A = 999;}void fun() {cout << "father的fun调用" << endl;}void fun(int a) {cout << "father的fun调用" << ' ' << a << endl;}
};
class son :public father{
public:int A;son() {A = 99;}void fun() {cout << "son的fun调用" << endl;}void fun(int a) {cout << "father的fun调用" << ' ' << a << endl;}
};
void fun() {son p;cout << "son" << ' ' << p.A << endl;cout << "father" << ' ' << p.father::A << endl;p.fun();p.fun(11);p.father::fun();p.father::fun(90);
}
int main() {fun();return 0;
}

 如果派生类中出现和基类重名的成员函数,那么派生类会隐藏基类全部重名的成员函数。需要注意的是,本来可以按照函数重载区分,却被派生类隐藏的情况:

代码: 

#include <iostream>
using namespace std;
class father {
public:int A;father() {A = 999;}void fun() {cout << "father的fun调用" << endl;}void fun(int a,int b) {cout << "father的fun调用" << ' ' << a << endl;}
};
class son :public father{
public:int A;son() {A = 99;}void fun() {cout << "son的fun调用" << endl;}
};
void fun() {son p;cout << "son" << ' ' << p.A << endl;cout << "father" << ' ' << p.father::A << endl;p.fun();p.fun();p.father::fun();//p.fun(90,89);//报错p.father::fun(89, 123);
}
int main() {fun();return 0;
}

六.继承同名static成员处理方式

 当重名的成员变量和函数是static时,需要注意通过类名来调用的方式

代码:

#include <iostream>
using namespace std;
class father {
public:static int A;static void fun() {cout << "father的fun调用" << endl;}void fun(int a) {cout << "father的fun调用" << a<<' ' << endl;}
};
int father::A=10;   //static成员变量,类内声明,类外定义
class son :public father {
public:static int A;static void fun() {cout << "son的fun调用" << endl;}
};
int son::A = 20;
void fun() {son p;//1.利用对象调用成员变量和函数cout << p.A << endl;cout << p.father::A << endl;p.fun();p.father::fun();cout << "**************************" << endl;//2.利用类名调用成员变量和函数cout << son::A << endl;cout << father::A << endl;cout << son::father::A << endl;   son::fun();father::fun();son::father::fun();    //father::fun(10);  //报错,此方法只能调用static类型}
int main() {fun();return 0;
}

七.多继承语法

C++中允许一个类继承多个类


语法: class子类∶继承方式父类1,继承方式父类2...


多继承可能会引发父类中有同名成员出现,需要加作用域区分
C++实际开发中不建议用多继承

 代码:

#include <iostream>
using namespace std;
class base1 {
public:int A;base1() {A = 10;cout << "base1构造函数调用" << endl;}
};class base2 {
public:int A;base2() {A = 20;cout << "base2构造函数调用" << endl;}
};class son :public base1, public base2 {
public:int B;int C;son() {B = 100;C = 200;cout << "son构造函数调用" << endl;}
};void fun() {son b;cout << b.base1::A << endl;cout << b.base2::A << endl;cout << b.B << endl;cout << b.C << endl;
}
int main() {fun();return 0;
}

 

 注意构造函数的调用顺序,父亲1先调用,父亲2再调用,最后son调用

总结:多继承中如果父类中出现了同名情况,子类使用时候要加作用域。

八.菱形继承

菱形继承概念:
两个派生类继承同一个基类
又有某个类同时继承者两个派生类
这种继承被称为菱形继承,或者钻石继承

1.羊继承了动物的数据,驼同样继承了动物的数据,当羊驼使用数据时,就会产生二义性。
2.羊驼将动物的数据继承了两份,其实我们应该清楚,这份数据我们只需要—份就可以。

 代码:

#include <iostream>
using namespace std;
class dongwu {
public:int A;
};
class yang :public dongwu {};
class tuo:public dongwu {};
class son :public tuo, public yang {};
void fun() {son p;p.yang::A = 100;p.tuo::A = 200;//当菱形继承,两个父亲拥有相同名的成员时,要加作用域加以区分;cout << p.yang::A << endl;cout << p.tuo::A << endl;
}
int main() {fun();return 0;
}

此时的继承情况是,这样的。 

 利用虚继承,可以解决菱形继承的问题(两份相同的数据,浪费内存)

代码:

#include <iostream>
using namespace std;
class dongwu {
public:int A;
};
//virtual虚继承,dongwu称为虚基类
class yang :virtual public dongwu {};
class tuo:virtual public dongwu {};
class son :public tuo, public yang {};
void fun() {son p;yang p1;tuo p2;p.yang::A = 100;p.tuo::A = 200;p1.A = 90;p2.A = 190;//当菱形继承,两个父亲拥有相同名的成员时,要加作用域加以区分;cout << p.yang::A << endl;cout << p.tuo::A << endl;cout << p1.A << endl;cout << p2.A << endl;}
int main() {fun();return 0;
}

 继承时只继承一份,此时son继承的是两个虚基类表的指针,虚基类表中存储的是到成员的偏移量。

总结:
·菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义·利用虚继承可以解决菱形继承问题

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

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

相关文章

2023全新TwoNav开源网址导航系统源码 | 去授权版

2023全新TwoNav开源网址导航系统源码 已过授权 所有功能可用 测试环境&#xff1a;NginxPHP7.4MySQL5.6 一款开源的书签导航管理程序&#xff0c;界面简洁&#xff0c;安装简单&#xff0c;使用方便&#xff0c;基础功能免费。 TwoNav可帮助你将浏览器书签集中式管理&#…

线程池启动线程 submit () 和 execute () 方法有什么不同?

ThreadPoolExecutor类是Java中用于创建和管理线程池的类,它提供了多个方法来提交任务给线程池,其中包括submit()和execute()方法。这两种方法有以下不同: 返回值:submit()方法返回一个Future对象,可以使用这个对象来跟踪任务的执行状态并获取任务的结果(如果有的话)。Fu…

JWT基础

概念 JSON Web Token本质上就是一串字符串&#xff0c;一串包含了很多信息的字符串令牌拥有三个部分头部-包含加密算法和令牌类型{"alg":"算法名称","type":"JWT"}负载-包含数据和信息-七个官方默认-也可以自己定义内容{iss&#xff…

ubuntu+.net6+docker 应用部署教程

先期工作 1、本地首先安装 Docker Desktop 2、本地装linux in windows 3、生成镜像 后期工作 1、云服务器部署 生成镜像方法 1、生成Dockerfile配置文件 开发工具visual studio 2022 如果项目已经存在&#xff0c;可以选中项目&#xff0c;右键点击->选择添加Docker…

基于Java的高校竞赛管理系统设计与实现(亮点:发起比赛、报名、审核、评委打分、获奖排名,可随意更换主题如蓝桥杯、ACM、王者荣耀、吃鸡等竞赛)

高校竞赛管理系统 一、前言二、我的优势2.1 自己的网站2.2 自己的小程序&#xff08;小蔡coding&#xff09;2.3 有保障的售后2.4 福利 三、开发环境与技术3.1 MySQL数据库3.2 Vue前端技术3.3 Spring Boot框架3.4 微信小程序 四、功能设计4.1 主要功能描述4.2 系统角色 五、系统…

基于Matlab实现图像配准技术(附上源码+图像)

图像配准是数字图像处理中的重要技术之一&#xff0c;它的目标是将多幅图像进行准确的对齐&#xff0c;使得它们在空间上保持一致。图像配准在许多领域都有广泛的应用&#xff0c;如医学影像、遥感图像、计算机视觉等。本文将介绍如何使用Matlab实现图像配准技术&#xff0c;并…

Flink相关

墨滴社区 用 Flink 取代 Spark Streaming&#xff01;知乎实时数仓架构演进_天池技术圈-阿里云天池 关于flink实时数仓的实际问题_flink datastream 按天,小时写入hdfs_一个写湿的程序猿的博客-CSDN博客 基于 Flink Hudi 的实时数仓在 Shopee 的实践 - 墨天轮

6.3 字符数组

思维导图&#xff1a; 前言&#xff1a; 主要内容&#xff1a; 前言内容整理 字符型数据和存储 字符型数据是依据字符的ASCII代码存储在内存单元中&#xff0c;通常占用一个字节的空间。ASCII代码可以被认为是整数&#xff0c;因此在C99标准中&#xff0c;字符类型被归类为整…

nodejs中的错误类型及捕获处理

nodejs中的错误类型及捕获处理 在 node 中&#xff0c;提供了 error 模块&#xff0c;并且内置了标准的 JavaScript 错误&#xff0c;本文将介绍在node中错误类型以及如何捕获。 错误类型 js错误 标准的js错误&#xff0c;跟在浏览器中运行js时提示的错误类型一样 RangeEr…

【Gradle-8】Gradle插件开发指南

1、前言 Gradle插件开发在Android进阶知识中是占有一定比例的&#xff0c;特别是在性能优化领域&#xff0c;基本都会涉及&#xff0c;而且跟我们日常的编译打包也息息相关&#xff0c;加上有不少招聘要求里也明确要有Gradle插件开发经验&#xff0c;所以即使大部分人的日常开…

SSH key 运作方式

1、本地创建SSH key pairs 2、把public key上传到网站服务器&#xff08;如GitHub 3、当使用ssh方式连接时 本地SSH client向远端请求ssh连接远端发来random data要求加密本地ssh client用private key加密&#xff0c;把加密的data发送过去&#xff08;不发送private key远端接…

I Pa?sWorD

2023icpc网络赛第一场 I 题意&#xff1a;题目给出只包含大小写字母&#xff0c;数字以及?的字符串&#xff0c;对于每一个小写字母&#xff0c;这一位字符既有可能是该小写字母&#xff0c;也有可能是该小写字母的对应大写字母&#xff0c;也就是该位的字符有两种可能&#x…

51单片机 矩阵键盘

按列扫描 #include <REGX52.H> #include "Delay.h"/*** brief 矩阵键盘读取按键键码* param 无* retval KeyNumber 按下按键的键码值如果按键按下不放&#xff0c;程序会停留在此函数&#xff0c;松手的一瞬间&#xff0c;返回按键键码&#xff0c;没有按键…

分布式ETL工具Sqoop实践

Mysql数据准备 1、在node02节点登录Mysql。 mysql -uroot -proot2、新建数据库testdb。 create database testdb;3、新建数据表ts。 use testdb; create table ts(id int, name varchar(10), age int, sex char(1));4、向表中插入数据。 insert into ts values(10001,张三…

【Java 基础篇】Java标准输入流详解:读取用户输入的完整指南

Java是一门流行的编程语言&#xff0c;常用于开发各种类型的应用程序&#xff0c;包括控制台应用、桌面应用、Web应用等。在这些应用中&#xff0c;与用户进行交互是一项重要的任务。本文将重点介绍Java标准输入流&#xff0c;它是Java程序中用于从用户获取输入的关键组成部分。…

Vue3函数式编程

文章目录 前言一、三种编程风格1.template2.jsx/tsx3.函数式编写风格 二、函数式编程1.使用场景2.参数3.例子3.render渲染函数 总结 前言 本文主要记录vue3中的函数式编程以及其他编程风格的简介 一、三种编程风格 1.template Vue 使用一种基于 HTML 的模板语法&#xff0c;…

如何下载安装 WampServer 并结合 cpolar 内网穿透,轻松实现对本地服务的公网访问

文章目录 前言1.WampServer下载安装2.WampServer启动3.安装cpolar内网穿透3.1 注册账号3.2 下载cpolar客户端3.3 登录cpolar web ui管理界面3.4 创建公网地址 4.固定公网地址访问 前言 Wamp 是一个 Windows系统下的 Apache PHP Mysql 集成安装环境&#xff0c;是一组常用来…

R语言画图

简单记录一下 plot(lad_profile_relative$lad, lad_profile_relative$height, type"l", lwd1.5, xlabexpression(paste("LAD ", "(", m^2, m^-3, ")" )), ylab"Height (m)")X轴数据&#xff0c; Y轴数据 type, 标记类型 lw…

Vue基础之模板语法介绍

前言 上篇我分享了关于Vue的入门&#xff0c;简单的入了个门。本篇文章将要分享的内容为Vue的模板语法。 一、插值 1.1、文本 1.2、html 1.3、属性 1.4、class、style绑定 1.5、表达式 在Vue的模板语法中&#xff0c;插值是一种常用的方式来动态地将数据渲染到视图中。Vue使用双…

华为云ROMA Connect亮相Gartner®全球应用创新及商业解决方案峰会,助力企业应用集成和数字化转型

9月13日-9月14日 Gartner全球应用创新及商业解决方案峰会在伦敦举行 本届峰会以“重塑软件交付&#xff0c;驱动业务价值”为主题&#xff0c;全球1000多位业内专家交流最新的企业应用、软件工程、解决方案架构、集成与自动化、API等企业IT战略和新兴技术热门话题。 9月13日…