doc/docs/CPlusPlus-main/25_C++的类中继承.md
sairate fa9377e4ae docs(book): 添加现代 C++教程及相关代码
- 新增现代 C++ 教程的 Preface 章节,包括英文和中文版本
- 添加 C++ Primer 练习代码
- 新增 Learn C++ 教程的 C++ 开发简介章节
- 添加头文件解析文档
- 更新 mkdocs.yml,包含新教程的目录结构
- 修改项目设置,使用 Python 3.10环境
2025-07-08 09:52:45 +08:00

944 lines
18 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# C++的继承
# 1. 继承简介
① 继承是面向对象的三大特性之一。
② 定义类时,下级别的成员除了拥有上一级的共性,还有自己的特性。这个时候,就可以考虑利用继承技术,减少重复代码。
![image.png](25_C++的类中继承_files/image.png)
## 1.1 普通实现
```python
#include <iostream>
using namespace std;
#include<string>
//打印输出类
class Java
{
public:
void header()
{
cout << "首页、公开课、登陆、注册...(公共头部)" << endl;
}
void footer()
{
cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
}
void left()
{
cout << "Java、Python、C++....(公共分类列表)" << endl;
}
void content()
{
cout << "Java学科视频" << endl;
}
};
class Python
{
public:
void header()
{
cout << "首页、公开课、登陆、注册...(公共头部)" << endl;
}
void footer()
{
cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
}
void left()
{
cout << "Java、Python、C++....(公共分类列表)" << endl;
}
void content()
{
cout << "Python学科视频" << endl;
}
};
class CPP
{
public:
void header()
{
cout << "首页、公开课、登陆、注册...(公共头部)" << endl;
}
void footer()
{
cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
}
void left()
{
cout << "Java、Python、C++....(公共分类列表)" << endl;
}
void content()
{
cout << "C++学科视频" << endl;
}
};
void test01()
{
cout << "Java下载视频页面如下" << endl;
Java ja;
ja.header();
ja.footer();
ja.left();
ja.content();
cout << "........................" << endl;
cout << "Python下载视频页面如下" << endl;
Python py;
py.header();
py.footer();
py.left();
py.content();
cout << "........................" << endl;
cout << "C++下载视频页面如下:" << endl;
CPP cpp;
cpp.header();
cpp.footer();
cpp.left();
cpp.content();
}
int main()
{
test01();
system("pause");
return 0;
}
```
运行结果:
- Java下载视频页面如下
- 首页、公开课、登陆、注册...(公共头部)
- 帮助中心、交流合作、站内地图...(公共底部)
- Java、Python、C++....(公共分类列表)
- Java学科视频
- ........................
- Python下载视频页面如下
- 首页、公开课、登陆、注册...(公共头部)
- 帮助中心、交流合作、站内地图...(公共底部)
- Java、Python、C++....(公共分类列表)
- Python学科视频
- ........................
- C++下载视频页面如下:
- 首页、公开课、登陆、注册...(公共头部)
- 帮助中心、交流合作、站内地图...(公共底部)
- Java、Python、C++....(公共分类列表)
- C++学科视频
- 请按任意键继续. . .
## 1.2 继承实现
```python
#include <iostream>
using namespace std;
#include<string>
//打印输出类
class BasePage
{
public:
void header()
{
cout << "首页、公开课、登陆、注册...(公共头部)" << endl;
}
void footer()
{
cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
}
void left()
{
cout << "Java、Python、C++....(公共分类列表)" << endl;
}
};
// 继承的好处减少重复代码
// 语法class 子类继承方式 父类
// 子类 也称为 派生类
// 父类 也称为 基类
//Java页面
class Java:public BasePage //继承了BasePage把BasePage里面的内容全部拿到手了
{
public:
void content()
{
cout << "Java学科视频" << endl;
}
};
//Python页面
class Python :public BasePage //继承了BasePage把BasePage里面的内容全部拿到手了
{
public:
void content()
{
cout << "Python学科视频" << endl;
}
};
//C++页面
class CPP :public BasePage //继承了BasePage把BasePage里面的内容全部拿到手了
{
public:
void content()
{
cout << "CPP学科视频" << endl;
}
};
void test01()
{
cout << "Java下载视频页面如下" << endl;
Java ja;
ja.header();
ja.footer();
ja.left();
ja.content();
cout << "........................" << endl;
cout << "Python下载视频页面如下" << endl;
Python py;
py.header();
py.footer();
py.left();
py.content();
cout << "........................" << endl;
cout << "C++下载视频页面如下:" << endl;
CPP cpp;
cpp.header();
cpp.footer();
cpp.left();
cpp.content();
}
int main()
{
test01();
system("pause");
return 0;
}
```
运行结果:
- Java下载视频页面如下
- 首页、公开课、登陆、注册...(公共头部)
- 帮助中心、交流合作、站内地图...(公共底部)
- Java、Python、C++....(公共分类列表)
- Java学科视频
- ........................
- Python下载视频页面如下
- 首页、公开课、登陆、注册...(公共头部)
- 帮助中心、交流合作、站内地图...(公共底部)
- Java、Python、C++....(公共分类列表)
- Python学科视频
- ........................
- C++下载视频页面如下:
- 首页、公开课、登陆、注册...(公共头部)
- 帮助中心、交流合作、站内地图...(公共底部)
- Java、Python、C++....(公共分类列表)
- CPP学科视频
- 请按任意键继续. . .
# 2. 三种继承改变权限
① 继承的语法class 子类:继承方式 父类
② 继承方式一共有三种:
1. 公共继承
2. 保护继承
3. 私有继承
③ 不同的继承方式,父类中的变量被继承后,权限相应的得到了改变,如下图所示。
![image.png](25_C++的类中继承_files/image.png)
```python
#include <iostream>
using namespace std;
#include<string>
//打印输出类
class Base1
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
//公共继承
class Son1:public Base1
{
public:
void func()
{
m_A = 10; //父类中的公共权限成员 到子类中依然是公共权限
m_B = 10; //父类中的保护权限成员 到子类中依然是保护权限
//m_C = 10; //父类中的私有权限成员 子类访问不到
}
};
void test01()
{
Son1 s1;
s1.m_A = 100; //公共权限类内能访问类外也能访问
//s1.m_B = 100; //保护权限类内能访问类外不能访问
}
//保护继承
class Son2:protected Base1
{
public:
void func()
{
m_A = 10; //父类中的公共权限成员 到子类中变为保护权限
m_B = 10; //父类中的保护权限成员 到子类中依然是保护权限
//m_C = 10; //父类中的私有权限成员 子类访问不到
}
};
void test02()
{
Son2 s2;
//s2.m_A = 100; //保护权限类内能访问类外不能访问
//s2.m_B = 100; //保护权限类内能访问类外不能访问
}
//私有继承
class Son3:private Base1
{
public:
void func()
{
m_A = 10; //父类中的公共权限成员 到子类中变为私有权限
m_B = 10; //父类中的保护权限成员 到子类中变为私有权限
//m_C = 10; //父类中的私有权限成员 子类访问不到
}
};
void test03()
{
Son3 s3;
//s3.m_A = 100; //私有权限类内能访问类外不能访问
//s3.m_B = 100; //私有权限类内能访问类外不能访问
}
int main()
{
system("pause");
return 0;
}
```
运行结果:
- 请按任意键继续. . .
# 3. 继承对象内存
## 3.1 查询继承对象所占内存
```python
#include <iostream>
using namespace std;
#include<string>
//继承中的对象模型
class Base
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
//公共继承
class Son:public Base
{
int m_D;
};
//利用开发人员命令提示工具查看对象模型
//跳转盘符F:
//跳转文件路径cd 具体路径下
//查看命令
//cl /d1 reportSingleClassLayout查看的类名 "文件名"
void test01()
{
//父类中所有非静态成员属性都会被子类继承下去
//父类中私有成员属性 是被编译器给隐藏了因此是访问不到但是确实被继承下去了
cout << "size of Son =" << sizeof(Son) << endl; //16个字节父类3个int一个12个字节字节1个int4个字节
}
int main()
{
test01();
system("pause");
return 0;
}
```
运行结果:
- size of Son =16
- 按任意键继续. . .
## 3.2 VS自带开发工具查询
① 首先打开visio studio的开发人员命令工具如下图所示。
![image.png](25_C++的类中继承_files/image.png)
② 查询visio studio的项目所在地址。
③ "输入跳转盘符例如C:"->"输入项目所在地的地址"->"输入dir(查询项目中文件信息)"
![image.png](25_C++的类中继承_files/image.png)
② 输入cl(空格)/d1 reportSingleClassLayout查看的类名 "文件名",可以查看类所占内存空间大小。
![image.png](25_C++的类中继承_files/image.png)
# 4. 继承构造和析构顺序
① 继承中,先调用父类构造函数,再调用子类构造函数,析构顺序与构造顺序相反,先调用子类析构函数,再调用父类析构函数。
```python
#include <iostream>
using namespace std;
#include<string>
//继承中的构造和析构顺序
class Base
{
public:
Base()
{
cout << "Base构造函数" << endl;
}
~Base()
{
cout << "Base析构函数" << endl;
}
};
//
class Son:public Base
{
public:
Son()
{
cout << "Son构造函数" << endl;
}
~Son()
{
cout << "Son析构函数" << endl;
}
};
void test01()
{
//Base b; //创建父类对象只有父类的构造函数析构函数
//继承中的构造和析构顺序如下
//先构造父类再构造子类析构的顺序与构造的顺序相反
Son s;
}
int main()
{
test01();
system("pause");
return 0;
}
```
运行结果:
- Base构造函数
- Son构造函数
- Son析构函数
- Base析构函数
- 请按任意键继续. . .
# 5. 同名成员处理
① 子类对象可以直接访问到子类中同名成员。
② 子类对象加作用域可以访问到父类同名成员。
③ 当子类与父类拥有同名的成员函数,子类会隐藏父类中所有同名成员函数(有参、无参),加作用域才可以访问到父类中同名函数。
```python
#include <iostream>
using namespace std;
#include<string>
//继承中同名成员处理
class Base
{
public:
Base()
{
m_A = 100;
}
int m_A;
void func()
{
cout << "Base - func()调用" << endl;
}
void func(int a)
{
cout << "Base - func(int a)调用" << endl;
}
};
class Son:public Base
{
public:
Son()
{
m_A = 200;
}
void func()
{
cout << "Son - func()调用" << endl;
}
int m_A;
};
//同名成员属性处理方式
void test01()
{
Son s;
cout << "Son 下 m_A=" << s.m_A << endl;
//如果通过子类对象访问到父类中同名成员需要加作用域
cout << "Base 下 m_A=" << s.Base::m_A << endl;
}
//同名成员函数处理方式
void test02()
{
Son s;
s.func(); //直接调用 调用时子类中的同名成员
//调用父类中同名成员函数
s.Base::func();
//如果子类中出现和父类同名的成员函数子类的同名成员会隐藏掉父类中所有同名成员函数
//如果想访问到父类中被隐藏的同名成员函数需要加作用域
s.Base::func(100);
}
//同名成员函数处理
int main()
{
test01();
test02();
system("pause");
return 0;
}
```
运行结果:
- Son 下 m_A=200
- Base 下 m_A=100
- Son - func()调用
- Base - func()调用
- Base - func(int a)调用
- 请按任意键继续. . .
# 6. 同名静态成员处理
① 静态成员和非静态成员出现同名,处理方式一致:
1. 访问子类同名成员,直接访问
2. 访问父类同名成员,需要加作用域
② 加上static关键字后成员发生变化成员变成静态成员。
③ 静态成员变量特点:
1. 所有对象都共享同一份数据。
2. 编译阶段就分配内存。
3. 类内声明,类外初始化。
④ 静态成员函数特点:
1. 只能访问静态成员变量,不能访问非静态成员变量。
2. 所有对象都共享同一份函数实例。
```python
#include <iostream>
using namespace std;
//继承中同名静态成员处理方式
class Base
{
public:
static int m_A; //静态成员类内声明类外初始化
static void func()
{
cout << "Base - static func()" << endl;
}
static void func(int a)
{
cout << "Base - static func(int a)" << endl;
}
};
int Base::m_A=100;
class Son:public Base
{
public:
static int m_A;
static void func()
{
cout << "Son - static void func()" << endl;
}
};
int Son::m_A = 200;
//同名静态成员属性
void test01()
{
//1通过对象访问
Son s;
cout << "Son 下 m_A = " << s.m_A << endl;
cout << "Base 下 m_A = " << s.Base::m_A << endl;
//2通过类名访问
cout << "Son 下 m_A = "<< Son::m_A << endl;
//第一个::代表通过类名方式访问 第二个::代表访问父类作用域
cout << "Base 下 m_A= "<< Son::Base::m_A << endl;
}
void test02()
{
//1通过对象访问
Son s;
s.func();
s.Base::func();
//2通过类名访问
Son::func();
Son::Base::func();
//子类出现和父类同名静态成员函数也会隐藏掉父类中所有同名成员函数
//如何想访问父类中被隐藏同名成员需要加作用域
Son::Base::func(100);
}
//同名成员函数处理
int main()
{
test01();
test02();
system("pause");
return 0;
}
```
运行结果:
- Son 下 m_A = 200
- Base 下 m_A = 100
- Son 下 m_A = 200
- Base 下 m_A= 100
- Son - static void func()
- Base - static func()
- Son - static void func()
- Base - static func()
- Base - static func(int a)
- 请按任意键继续. . .
# 7. 多继承语法
① C++运行一个类继承多个类。
② 语法class 子类:继承方式 父类1继承方式 父类2.....
③ 多继承可能会引发父类中有同名成员出现,需要加作用域区分。
④ C++实际开发中不建议用多继承。
```python
#include <iostream>
using namespace std;
class Base1
{
public:
Base1()
{
m_A = 100;
}
int m_A;
};
class Base2
{
public:
Base2()
{
m_A = 200;
}
int m_A;
};
//子类 需要继承Base1和Base2
//语法class 子类继承方式 父类1继承方式 父类2.....
class Son:public Base1,public Base2
{
public:
Son()
{
m_C = 300;
m_D = 400;
}
int m_C;
int m_D;
};
void test01()
{
Son s;
cout << "sizeof(Son):" << sizeof(s) << endl;
//当父类中出现同名成员需要加作用域区分
cout << "Base1::m_A = " << s.Base1::m_A << endl;
cout << "Base2::m_A = " << s.Base2::m_A << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
```
运行结果:
- sizeof(Son):16
- Base1::m_A = 100
- Base2::m_A = 200
- 请按任意键继续. . .
# 8. 菱形继承
## 8.1 菱形继承简介
① 菱形继承概念:
1. 两个派生类继承同一个基类
2. 又有某个类同时继承两个派生类
3. 这种继承被称为菱形继承
② 羊继承了动物的数据,驼同样继承了动物的数据,当草泥马使用数据是,就会产生二义性。
③ 草泥马继承自动物的数据继承了两份,其实我们应当清楚,这份数据我们只需要一份就可以。
④ 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义。
⑤ 利用虚继承可以解决菱形继承问题。
![image.png](25_C++的类中继承_files/image.png)
## 8.2 菱形继承普通方式
```python
#include <iostream>
using namespace std;
//动物类
class Animal
{
public:
int m_Age;
};
//羊类
class Sheep:public Animal{};
//驼类
class Tuo:public Animal{};
//羊驼类
class SheepTuo:public Sheep,public Tuo{};
void test01()
{
SheepTuo st;
st.Sheep::m_Age = 18;
st.Tuo::m_Age = 28;
//当出现菱形继承两个父类拥有相同数据需要加以作用域区分
cout << "st.Sheep::m_Age="<< st.Sheep::m_Age << endl;
cout << "st.Tuo::m_Age=" << st.Tuo::m_Age << endl;
//这份数据我们知道 只有一份就可以菱形继承导致数据有两份资源浪费
}
int main()
{
test01();
system("pause");
return 0;
}
```
运行结果:
- st.Sheep::m_Age=18
- st.Tuo::m_Age=28
- 请按任意键继续. . .
## 8.3 菱形继承虚继承
### 8.3.1 菱形继承虚继承方式
```python
#include <iostream>
using namespace std;
//动物类
class Animal
{
public:
int m_Age;
};
//利用虚继承 解决菱形继承的问题
//继承之前 加上关键字 virtual 变成 虚继承
//虚继承后Animal类 称为 虚基类
//羊类
class Sheep:virtual public Animal{};
//驼类
class Tuo:virtual public Animal{};
//羊驼类
class SheepTuo:public Sheep,public Tuo{};
void test01()
{
SheepTuo st;
st.Sheep::m_Age = 18;
st.Tuo::m_Age = 28;
//当出现菱形继承两个父类拥有相同数据需要加以作用域区分
cout << "st.Sheep::m_Age="<< st.Sheep::m_Age << endl;
cout << "st.Tuo::m_Age=" << st.Tuo::m_Age << endl;
//虚继承多产生一种输出方式(因为上面两个是同一份数据所以可以不加作用域来区分了)
cout << "st.m_Age=" << st.m_Age << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
```
运行结果:
- st.Sheep::m_Age=28
- st.Tuo::m_Age=28
- st.m_Age=28
- 请按任意键继续. . .
### 8.3.2 菱形继承虚继承VS查询
① Sheep类和Tuo类继承的都是vbptrvbptr为虚基类继承指针。
1. v - virtual
2. b - base
3. ptr = pointer
② 菱形继承并不是继承两份数据,而是继承两份指针,这两个指针会分别指向虚基类表,虚基类表中会记录偏移量,加上这个偏移量,会指向这个唯一的数据。
![image.png](25_C++的类中继承_files/image.png)