类 —— 继承、多重继承

继承

一个类,继承另一个已有的类。(在一个已存在的类的基础上建立一个新的类,并拥有其特性)
是一个父类(基类)派生出子类(派生类)的过程。
派生类往往是基类的具象化,基类则是派生类的抽象。
基类与派生类是相对的,一个类可能存在又是基类又是派生类的情况。
继承提高了代码的复用性

在实际的使用中,派生类会做出一些与基类的差异化:
● 修改继承来自的基类内容
属性:公有属性直接修改,私有属性通过接口修改
函数:函数隐藏,通过派生类实现一个同名同参数的函数,来隐藏基类的函数。
● 新增派生类的内容

#include <iostream>
using namespace std;// 基类
class Father
{
private:string name = "张";public:void set_name(const string &name){this->name = name;}string get_name(){return name;}void work(){cout << "我的工作是厨师,我负责炒菜。" << endl;}
};// 派生类继承基类
class Son:public Father
{
public:void init(){set_name("王");				// 私有属性通过接口修改}void work(){cout << "我是飞行员,我要上天。" << endl;}void game(){cout << "三硝基甲苯。" << endl;}
};int main()
{Son s;cout << s.get_name() << endl; 			// 张s.init();cout << s.get_name() << endl; 			// 王s.work();				// s.game();// 调用基类被隐藏的成员函数s.Father::work();return 0;
}

继承的格式、权限

class 类名:父类名 {};					// 默认权限是 private
class 类名:继承的权限 父类名 {};

在这里插入图片描述
在这里插入图片描述

继承时类中的特殊成员函数不会被继承

构造函数

派生类中的任意一个构造函数,都必须直接或者间接调用基类的一个构造函数。

problem:
#include <iostream>
using namespace std;class Father
{string secret;
public:string firstname;float asset;Father(string secret, string fname, float asset):secret(secret), firstname(fname), asset(asset){cout << "A constructor of father class with parameters. " << endl;}
};class Son:public Father
{string secret;
public:void show(){cout << "First name: " << firstname << endl;}};int main()
{// Son s1;  				// 找不到基类的无参构造函数    // error,因为父类只有有参构造,而子类中没有提供 能够调用父类有参构造的构造函数,不能成功创建父类的空间// Son s2("~", "0", 1);		// 找不到匹配的构造函数cout << "Size of father: " << sizeof(Father) << endl;cout << "Size of son: " << sizeof(Son) << endl;return 0;
}

在这里插入图片描述
在这里插入图片描述

solution1:补充基类的无参构造函数
#include <iostream>
using namespace std;class Father
{string secret;
public:string firstname;float asset;Father():secret("~"), firstname("w"), asset(1000000) { }Father(string secret, string fname, float asset):secret(secret), firstname(fname), asset(asset){cout << "A constructor of father class with parameters. " << endl;}
};class Son:public Father
{string secret;
public:void show(){cout << "First name: " << firstname << endl;}};int main()
{Son s1;s1.show();// Son s2("~", "0", 1);		// 找不到匹配的构造函数cout << "Size of father: " << sizeof(Father) << endl;cout << "Size of son: " << sizeof(Son) << endl;return 0;
}

在这里插入图片描述

solution2:透传构造

需要 在子类构造函数的初始化列表中 显性调用父类的构造函数
当父类中只有有参构造,子类在创建类对象时,必须手动调用父类的构造函数。
在这里插入图片描述

#include <iostream>
using namespace std;class Father
{string secret;
public:string firstname;float asset;Father(string fname, float asset):firstname(fname), asset(asset){cout << "A constructor of father class with parameters. " << endl;}
};class Son:public Father
{string secret;
public:						Son():Father("default string", 120000)//, secret("0")		// 这里使用具体的值{ }// Father(fname, asset) 和 secret(secret) 可以交换,但有警告Son(string fname, float asset, string secret):Father(fname, asset), secret(secret){}void show(){cout << "First name: " << firstname << endl;cout << "Asset: " << asset << endl;cout << "Secret of the son: " << secret << endl;}
};int main()
{Son s1("Xing", 120000, "secret1");s1.show();Son s2;s2.show();return 0;
}

在这里插入图片描述

solution3:继承构造(C++ 11 支持)

C++支持 不用在子类中再写一遍父类的构造函数 ——

using Father::Father; 		// 在子类中使用父类的构造函数

直接使用继承构造的方式,不能对子类成员初始化
继承构造本质上并不是把父类中的构造函数继承给子类,编译器自动根据父类中构造函数的格式,提供出派生的构造函数(个数、参数都和父类中的构造函数一致),主要还是通过透传构造创建父类的空间。
在这里插入图片描述

#include <iostream>
using namespace std;class Father
{string secret;
public:string firstname;float asset;Father():secret("~"), firstname("w"), asset(1000000){cout << "A constructor of father class with no parameters. " << endl;}Father(float asset):asset(asset){cout << "A constructor of father class with only one parameter. " << endl;}Father(string fname, float asset):firstname(fname), asset(asset){cout << "A constructor of father class with two parameters. " << endl;}Father(Father &other):firstname(other.firstname), asset(other.asset)	// 拷贝构造{cout << "Duplicator_constructor of father class. " << endl;}
};class Son:public Father
{string secret;using Father::asset;        // 把父类中公有继承下来的 asset 成员,在子类中改成私有权限using Father::Father;public:void show(){cout << "First name: " << firstname << endl;cout << "Asset: " << asset << endl;cout << "Secret of the son: " << secret << endl;}
};int main()
{Son s0;s0.show();Son s1(120000);s1.show();Son s2("Xing", 120000);s2.show();Son s3 = s2;s3.show();return 0;
}

在这里插入图片描述

solution4:委托构造

一个类的构造函数可以调用这个类中的另一个构造函数,但是要避免循环委托
委托构造的性能低于透传构造,但是代码维护性更好,因为通常一个类中构造函数都会委托给能力最强(参数最多)的构造函数,代码重构时,只需要更改这个参数最多的构造函数即可。

只有一个类

并不直接通过无参构造实例化对象,而是无参构造委托有参构造,从而实例化对象。
在这里插入图片描述

#include <iostream>
using namespace std;class Son
{string secret;
public:Son():Son("secret0"){cout << "A constructor of child class without parameters. " << endl;}Son(string secret):secret(secret){cout << "A constructor of child class with parameter(s). " << endl;}void show(){cout << "Secret of the son: " << secret << endl;}
};int main()
{Son s0;s0.show();Son s1("secret1");s1.show();return 0;
}

在这里插入图片描述

父子类的继承

在这里插入图片描述

#include <iostream>
using namespace std;class Father
{string secret;
public:string firstname;float asset;Father(string fname, float asset):firstname(fname), asset(asset){cout << "A constructor of father class with two parameters. " << endl;}Father(float asset):asset(asset){cout << "A constructor of father class with only one parameter. " << endl;}
};class Son:public Father
{string secret;public:Son():Son("secret0"){cout << "A constructor of child class without parameters. " << endl;}Son(string secret):Father("Xing", 120000), secret(secret){cout << "A constructor of child class with parameter(s). " << endl;}void show(){cout << "First name: " << firstname << endl;cout << "Asset: " << asset << endl;cout << "Secret of the son: " << secret << endl;}
};int main()
{Son s0;s0.show();return 0;
}

在这里插入图片描述

拷贝构造函数

需要在初始化列表中显性调用父类的拷贝构造,传 other 对象到父类的拷贝构造中。

#include <iostream>
using namespace std;class Father
{string secret;
public:string firstname;float asset;Father(string fname, float asset):firstname(fname), asset(asset){cout << "A constructor of father class with two parameters. " << endl;}Father(float asset):asset(asset){cout << "A constructor of father class with only one parameter. " << endl;}Father(Father &other):firstname(other.firstname), asset(other.asset){cout << "Duplicator_constructor of father class. " << endl;}
};class Son:public Father
{string secret;public:Son(string fname, float asset, string secret):Father(fname, asset), secret(secret){}Son(Son &other):Father(other), secret(other.secret){cout << "Duplicator_constructor of child class. " << endl;}void show(){cout << "First name: " << firstname << endl;cout << "Asset: " << asset << endl;cout << "Secret of the son: " << secret << endl;}
};int main()
{Son s1("Xing", 120000, "secret1");s1.show();Son s2 = s1;s2.show();return 0;
}

在这里插入图片描述

继承时构造和析构的时机

子类在继承父类时,会先把父类中的成员保留一份,再来创建子类自己的成员。
父类先构造,子类后构造。子类先析构,父类后析构。

#include <iostream>
using namespace std;class Father
{string secret;
public:string firstname;float asset;Father(string fname, float asset):firstname(fname), asset(asset){cout << "A constructor of father class with two parameters. " << endl;}Father(float asset):asset(asset){cout << "A constructor of father class with only one parameter. " << endl;}~Father()				// 注意这里没有用虚析构函数{cout << "Destructor of father class. " << endl;}
};class Son:public Father
{string secret;
public:Son():Father("default string", 120000)          // 这里使用具体的值{cout << "A constructor of child class with no parameters. " << endl;}Son(string fname, float asset, string secret):Father(fname, asset), secret(secret){cout << "A constructor of child class with parameters. " << endl;}~Son(){cout << "Destructor of child class. " << endl;}
};int main()
{Son *s = new Son;delete s;s = nullptr;cout << endl;Son *ss = new Son("z", 1200000, "0");delete ss;ss = nullptr;cout << endl;Father *sss = new Son();delete sss;sss = nullptr;cout << endl;Father *ssss = new Son("z", 1200000, "0");delete ssss;ssss = nullptr;cout << endl;return 0;
}

在这里插入图片描述
在这里插入图片描述

当父子类中存在同名成员

访问时不会发生冲突,默认访问子类的成员变量。

#include <iostream>
using namespace std;class Father
{string secret;
public:string firstname;float *asset;Father():asset(new float){cout << "A constructor of father class without parameters. " << endl;}~Father(){delete asset;cout << "Destructor of father class. " << endl;}
};class Son:public Father
{string secret;public:float *asset;Son():asset(new float){cout << "A constructor of child class without parameters. " << endl;}~Son(){delete asset;cout << "Destructor of child class. " << endl;}};int main()
{Son *m = new Son;*(m->asset) = 140000;cout << "*(m->asset): " << *(m->asset) << endl;cout << "*(m->Father::asset): " << *(m->Father::asset) << endl;delete m;m = nullptr;return 0;
}

在这里插入图片描述

多重继承

一个子类,继承自多个基类。

格式

class 类名:继承权限 父类名, 继承权限 父类名, …
{
};

1、当多个父类中包含同名成员

多个父类中包含同名成员,通过 域限定符 访问指定的父类中成员。

#include <iostream>
using namespace std;class Father
{string secret;
public:void act_1(){cout << "Earn money. " << endl;}
};class Son:public Father
{string secret;public:void act_1(){cout << "Save money. " << endl;}void act_2(){cout << "Inherit money. " << endl;}
};class Grandchild:public Father, public Son
{
};int main()
{Son s;s.act_1();s.Father::act_1();Grandchild g;g.act_2();g.Son::act_2();return 0;
}

在这里插入图片描述

2、菱形继承(钻石继承)

     A                ------>		公共基类/   \B     C             ------>		中间子类\   /D                ------>		汇集子类

汇集子类中,会包含多份公共基类中的内容。

Problems ——>

1、会发生二义性的问题(同一个变量或者函数,可以通过两种方法访问)
2、如果公共基类过大,会造成汇集子类中的代码 膨胀 / 冗余。

#include <iostream>
using namespace std;class A
{
public:int a;
};class B:public A
{
};class C:public A
{
};class D:public C, public B
{
};int main()
{D d0;d0.C::a = 100;cout << "In C: " << d0.C::a << endl;		// 100d0.B::a = 90;   				// 可以直接通过中间子类访问,直接访问 B 中的 a成员cout << "In B: " << d0.B::a << endl;		// 90// d0.B::A::a = 80;				// 以下四行报错// cout << d0.B::A::a << endl;	// d0.C::A::a = 70;// cout << d0.C::A::a << endl;  // 会发生二义性,因为有两条路径都可以访问到 Areturn 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

虚继承(virtual)

虚继承,指对公共基类的虚继承。
主要用于解决菱形继承问题,采用虚继承后,公共基类中的内容,只会在汇集子类中存在一份,在汇集子类中,可以通过任意一条路径访问到公共基类中的成员。在中间子类继承时加 virtual。
虚继承解决二义性主要通过虚基类指针和虚基类表实现,下例中的 类B、类C 会分别创建一个虚基类指针和虚基类表。所有同类型对象共用一张虚基类表,每个对象内部增加一个隐藏的虚基类指针成员变量,这个虚基类指针指向虚基类表。派生类D 中,也有隐藏的虚基类指针,但是没有自己虚基类表。在调用 A的成员时, D对象 会通过虚基类指针找到对应的虚基类表,通过查表避免二义性。

——> Solution
#include <iostream>
using namespace std;class A
{
public:int a;
};class B:virtual public A
{
};class C:virtual public A
{
};class D:public B, public C
{
};int main()
{D d0;d0.B::A::a = 90;cout << d0.C::A::a << endl;			// 成功打印 90,上一个问题得以解决return 0;
}
为每一个类添加构造函数
#include <iostream>
using namespace std;class A
{
public:int a;A(int a):a(a) {cout << "A" << endl; }
};class B:virtual public A
{
public:int b;B(int a, int b):A(a), b(b) {cout << "B" << endl; }
};class C:virtual public A
{
public:C(int a):A(a) {cout << "C" << endl; }	// B 和 C 哪一个先构造,取决于 D 在继承时的顺序
};class D:public C, public B			// 决定结果的顺序 为 C、B
{
public:D(int a, int b):B(a, b), C(a), A(a) {cout << "D" << endl; }		// B、C 可交换顺序,结果不变
};int main()
{D d0(12, 5);cout << d0.a << '\t' << d0.b << endl;d0.B::A::a = 90;cout << d0.C::A::a << endl;return 0;
}

在这里插入图片描述

#include <iostream>
using namespace std;class A
{
public:int a;A(int a):a(a) {cout << "A" << endl; }
};class B:virtual public A
{
public:int b;B(int a, int b):A(a), b(b) {cout << "B" << endl; }
};class C:virtual public A
{
public:C(int a):A(a) {cout << "C" << endl; }	// B 和 C 哪一个先构造,取决于 D 在继承时的顺序
};class D:public B, public C
{
public:D(int a, int b):C(a), B(a, b), A(a) {cout << "D" << endl; }		// B、C 可交换顺序
};int main()
{D d0(12, 5);cout << d0.a << '\t' << d0.b << endl;d0.B::A::a = 90;cout << d0.C::A::a << endl;return 0;
}

在这里插入图片描述

B 和 C 哪一个先构造,取决于 D 在继承时的顺序。

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

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

相关文章

基于GAN的多尺度门合并多模态MRI图像合成

Multi-Modal MRI Image Synthesis via GAN With Multi-Scale Gate Mergence 基于GAN的多尺度门合并多模态MRI图像合成背景贡献实验方法生成器gate mergence (GM) strategy&#xff08;门控融合策略&#xff09;判别器 损失函数Thinking 基于GAN的多尺度门合并多模态MRI图像合成…

WPF实战项目十九(客户端):修改RestSharp的引用

修改HttpRestClient&#xff0c;更新RestSharp到110.2.0&#xff0c;因为106版本和110版本的代码不一样&#xff0c;所以需要修改下代码 using Newtonsoft.Json; using RestSharp; using System; using System.Threading.Tasks; using WPFProjectShared;namespace WPFProject.S…

java springboot通过application配置文件生成随机值并控制范围

我们找到 项目的 application 配置文件 这里我们还是习惯用 yml格式的 我们在配置文件中 写出 ${random.} 的时候 他就会将所有可配置的随机类型都提示出来了 有 整数 长整星 字符串 uuid 这里 我们来个模板 testcase:book:id: ${random.int}name: ${random.value}date: ${r…

数据库的多表查询(MYSQL)表表联立

根据以上三张表格&#xff0c;对三张表格进行不同的联立&#xff0c;查询并显示符合条件的内容。 1. 查出至少有一个员工的部门。显示部门编号、部门名称、部门位置、部门人数。 mysql> SELECT d.deptno AS 部门编号, d.dname as 部门名称, d.loc as 部门位置, COUNT(e.emp…

98.套接字-Socket网络编程1(基础概念)

目录 1.局域网和广域网 2.IP 互联网协议(Internet Protocol) IP的作用 3.查看IP地址 Windows上查看IP ​编辑 Linux上查看IP 4.端口 主要类型&#xff1a; 用途&#xff1a; 示例&#xff1a; 端口的表示&#xff1a; 5.OSI/ISO 网络分层模型 1.局域网和广域网 …

手持机|三防智能手机_4寸/5寸/6寸安卓系统三防手机PDA手持终端方案

随着科技的不断发展&#xff0c;三防手持机作为一种多功能设备&#xff0c;正逐渐在各行业得到广泛应用。这款手持机采用高性能处理器&#xff0c;支持高精度北斗定位和工业本安防爆功能&#xff0c;并具备IP67级防水防尘性能和1.5米防跌落能力。因此&#xff0c;它在仓储管理、…

Adobe系列的冷门成员~Firework

本贴博主给大家带来的是一款上了年纪、且比较冷门的Adobe全家桶成员——Firework&#xff0c;对于网页设计的从业者来说还是有一定的应用价值的&#xff0c;快来一起看看吧&#xff01; 一款网页作图软件&#xff0c;软件可以加速 Web 设计与开发&#xff0c; 是一款创建与优化…

QT QGraphicsItem 图元覆盖导致鼠标点击事件不能传递到被覆盖图元

一、概述 在日常开发中&#xff0c;遇到这样一个问题&#xff0c;线图元和引脚图元重叠&#xff0c;导致点击引脚图元&#xff0c;没有进入引脚图元的鼠标点击事件中。 二、产生原因 如果您的 QGraphicsItem 上有一个图元覆盖了它&#xff0c;可能会导致鼠标事件无法正常触发…

L1-005:考试座位号

题目描述 每个 PAT 考生在参加考试时都会被分配两个座位号&#xff0c;一个是试机座位&#xff0c;一个是考试座位。正常情况下&#xff0c;考生在入场时先得到试机座位号码&#xff0c;入座进入试机状态后&#xff0c;系统会显示该考生的考试座位号码&#xff0c;考试时考生需…

百度地图JavaScript API GL获取经纬度,标记,添加文本标注,点击事件,封装

百度地图JavaScript API GL常用方法封装 引入百度js库 <script type"text/javascript" src"https://api.map.baidu.com/api?v1.0&typewebgl&ak自己的百度应用ak"></script>封装方法 <template><div class"map"&…

Flink(八)【窗口】

前言 终于忙完了四门专业课的期末&#xff0c;确实挺累啊。今天开始继续学习 Flink &#xff0c;接着上次的内容。 今日摘录&#xff1a; 他觉得一个人奋斗更轻松自在。跟没有干劲的人在一起厮混&#xff0c;只会徒增压力。 -《解忧杂货店》 1、窗口 之前我们已经了解了…

Redis7--基础篇4(Redis事务)

Redis事务是什么 可以一次执行多个命令&#xff0c;本质是一组命令的集合&#xff0c;一个事务中的所有命令都会序列化&#xff0c;按顺序串行&#xff0c;而不会被其他命令插入。 其作用就是在一个队列中&#xff0c;一次性、顺序、排他的执行一系列命令。 Redis事务 VS 数据…