🎁个人主页:工藤新一¹
🔍系列专栏:C++面向对象(类和对象篇)
🌟心中的天空之城,终会照亮我前方的路
🎉欢迎大家点赞👍评论📝收藏⭐文章
文章目录
六、继承
-
继承是面向对象的三大特性之一
-
优点:减少大量而重复的代码
-
语法:
class 子类(派生类) : 继承方式 父类(基类)
-
派生类中的成员包含两大部分:
1.一类是从基类继承过来的,另一类是自己增添的成员
2.从基类继承过来的特性表现其共性,而新增的成员体现了其个性
6.1继承的基本语法
- 例如在我们看到的很多网站中,都有公共的头部,公共的底部,或者公共的左右侧列表,只有中间内容各显神态
- 接下来我们分别要利用普通写法和继承的方式来实现网页中的内容,来对比一下继承存在的意义,以及其所具有的便利性
- 写一个普通实现的 Java教学页面
#include<iostream>
using namespace std;
class Java {
public:
void header(){
cout << "首页、公开课、登录、注册...(公共头部信息)" << endl;
}
void footer(){
cout << "帮助中心、交流合作、站内地图...(公共底部信息)" << endl;
}
void left(){
cout << "Java、C++、C、Python...(公共分类列表)" << endl;
}
void content() {
cout << "Java 学科视频(页面内容)" << endl;
}
};
void test1()
{
cout << "Java 下载视频页面如下:" << endl;
Java ja;
ja.header();
ja.footer();
ja.left();
ja.content();
}
int main(){test1();return 0;}
- Python、C++ 教学页面
#include<iostream>
using namespace std;
class Java {
public:
void header(){
cout << "首页、公开课、登录、注册...(公共头部信息)" << endl;
}
void footer(){
cout << "帮助中心、交流合作、站内地图...(公共底部信息)" << endl;
}
void left(){
cout << "Java、C++、C、Python...(公共分类列表)" << endl;
}
void content() {
cout << "Java 学科视频(页面内容)" << endl;
}
};
class Python {
public:
void header() {
cout << "首页、公开课、登录、注册...(公共头部信息)" << endl;
}
void footer() {
cout << "帮助中心、交流合作、站内地图...(公共底部信息)" << endl;
}
void left() {
cout << "Java、C++、C、Python...(公共分类列表)" << endl;
}
void content() {
cout << "Python 学科视频(页面内容)" << endl;
}
};
class Cpp {
public:
void header() {
cout << "首页、公开课、登录、注册...(公共头部信息)" << endl;
}
void footer() {
cout << "帮助中心、交流合作、站内地图...(公共底部信息)" << endl;
}
void left() {
cout << "Java、C++、C、Python...(公共分类列表)" << endl;
}
void content() {
cout << "Python 学科视频(页面内容)" << endl;
}
};
void test1()
{
cout << "Java 下载视频页面如下:" << endl;
Java ja;
ja.header();
ja.footer();
ja.left();
ja.content();
cout << "-----------------------------------" << endl;
}
void test2() {
cout << "Python 下载视频页面如下:" << endl;
Python py;
py.header();
py.footer();
py.left();
py.content();
cout << "-----------------------------------" << endl;
}
void test3()
{
cout << "Cpp 下载视频页面如下:" << endl;
Cpp cpp;
cpp.header();
cpp.footer();
cpp.left();
cpp.content();
cout << "-----------------------------------" << endl;
}
int main(){test1();test2();test3();return 0;}
- 其实我们可以看到,通过笨拙的方法依然可以得到我们想要的内容,但这样的方法不管是在效率上、空间上…都不尽人意。
- 继承的高效性 — 继承实现页面
#include<iostream>
using namespace std;
class BasePage//公共部分
{
public:
void header(){
cout << "首页、公开课、登录、注册...(公共头部信息)" << endl;
}
void footer(){
cout << "帮助中心、交流合作、站内地图...(公共底部信息)" << endl;
}
void left(){
cout << "Java、C++、C、Python...(公共分类列表)" << endl;
}
};
//Java页面:Java 继承(:) BasePage的内容
class Java : public BasePage {
public:
void content() {
cout << "Java 学科视频" << endl;
}
};
//Pthyon页面:Pthyon 继承(:) BasePage
class Python : public BasePage {
public:
void content() {
cout << "Pthyon 学科视频" << endl;
}
};
//Cpp页面:Cpp 继承(:) BasePage
class Cpp : public BasePage {
public:
void content() {
cout << "C++ 学科视频" << endl;
}
};
void test() {
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();
cout << "-----------------------------------" << endl;
}
int main(){test();return 0;}
6.2继承方式
-
继承语法:class 派生类 : 继承方式 基类
-
继承方式一个有三种:
- 公共继承
- 保护继承
- 私有继承
-
继承方式图示:
- 公共继承
- 保护继承
- 子类所继承的父类可访问的成员(或行为)变为Protected
- 私有继承
- 子类继承的可访问的父类成员变为Private
6.3继承中的对象模型
- 问题:从父类继承而来的成员,哪些属于父类?
#include<iostream>
using namespace std;
class Base {
public:
int m_A;
protected:
int m_B;
private:
int m_C;//父类中的私有成员,在子类被隐藏了,但仍可被子类继承
};
//公共继承
class Son1 : public Base {
public:
int m_D;
};
void test01() {
cout << "sizeof Son1 == " << sizeof(Son1) << endl;
}
int main(){test01();return 0;}
- 根据代码块的输出,我们可以验证:
父类中私有成员,在子类被隐藏了,但仍可被子类继承
- **结论:**父类中所有非静态成员属性都会被子类继承下去
- 继承对象模型分布图: VS开发人员命令提示工具查看对象模型
- **跳转盘符 F: **
- 跳转文件路径:cd 具体路径
- 查看命令行:cl / d1 reportSingleClassLayout 类名 “文件名”
- 结论:父类中的私有成员,同样被子类所继承,只是编译器将其隐藏,因此我们访问不到父类中的私有成员
6.4继承中构造与析构顺序
-
子类继承父类后,当创建子类对象时,也会调用父类中的构造函数
-
问题:父、子类间的构造和析构顺序先后顺序?
-
先构造父类,再构造子类,且在调用子类时,会优先调用子类所属的父类的构造函数,反之调用析构函数
#include<iostream>
using namespace std;
class Base {
public:
Base() {
cout << "Base 的构造函数调用!" << endl;
}
~Base() {
cout << "Base 的析构函数调用!" << endl;
}
};
class Son : public Base {
public:
Son() {
cout << "Son 的构造函数调用!" << endl;
}
~Son() {
cout << "Son 的析构函数调用!" << endl;
}
};
int main(){ Son s;return 0; }
- 图示原理:
6.5同名继承的处理方式
-
问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的对象呢?
-
访问子类同名成员,直接访问即可
-
访问父类同名成员,需加入作用域
6.5.1同名成员
- 同名成员变量(成员属性):
#include<iostream>
using namespace std;
class Base {
public:
Base() {
m_A = 100;
}
int m_A;
};
class Son : public Base {
public:
Son(){
m_A = 200;
}
int m_A;
};
void test() {
Son s;
cout << "Son 下的 s.m_A == " << s.m_A << endl;
cout << "Base 下的 s.m_A == " << s.Base::m_A << endl;
}
int main(){test();return 0;}
- 同名成员函数(成员行为):
class Base {
public:
void func() {
cout << "Base - func 的调用!" << endl;
}
int m_A;
};
class Son : public Base {
public:
int m_A;
};
//当不存在 Son - func() 时,子类 Son 所实例化出 s,可以直接访问父类 Base 中的公共属性、行为
void test() { Son s; s.func(); }
- 当子类与父类具有同名的成员函数 - 通过对象直接调用,子类会优先调用自身的成员函数,因此需要以作用域,加以区分
#include<iostream>
using namespace std;
class Base {
public:
void func() {
cout << "Base - func 的调用!" << endl;
}
//函数重载:同一作用域下,函数的名称相同,但ha
void func(int) {
cout << "Son - func(int) 的调用!" << endl;
}
int m_A;
};
class Son : public Base {
public:
void func() {
cout << "Son - func 的调用!" << endl;
}
int m_A;
};
void test() {
Son s;
s.func();
s.Base::func();
s.Base::func(100);//函数重载
}
6.5.2继承同名静态成员处理方式
-
问题:继承中同名的静态成员在子类对象上如何进行访问?
-
静态成员与非静态成员同名,处理方式一致
-
访问子类同名成员,直接访问即可
-
访问父类同名成员,需加入作用域
-
-
静态同名成员属性:
- 通过对象访问
- 通过类名访问
#include<iostream>
using namespace std;
class Base {
public:
static int m_A;//类内声明,类外初始化
};
int Base::m_A = 100;
class Son : public Base {
public:
static int m_A;
};
int Son::m_A = 200;
void test() {
//1.通过对象访问数据
cout << "通过对象访问:" << endl;
Son s;
cout << "Son - m_A == " << s.m_A << endl;
cout << "Base - m_A == " << Base::m_A << endl;
//2.通过类名访问数据
cout << "通过类名访问数据:" << endl;
cout << "Son - m_A == " << Son::m_A << endl;
//Son::(通过Son类名的访问方式) - Base::m_A(访问父类Base作用域下的m_A)
cout << "Base - m_A == " << Son::Base::m_A << endl;
}
int main(){test();return 0;}
-
同名静态成员函数(行为):
-
通过对象访问
-
通过类名访问
-
#include<iostream>
using namespace std;
class Base {
public:
static void func() {
cout << "Base - static func()的调用!" << endl;
}
static void func(int) {
cout << "Base - static func(int)的调用!" << endl;
}
};
class Son : public Base {
public:
static void func() {
cout << "Son - static func()的调用!" << endl;
}
};
void test() {
//1.通过对象访问
cout << "通过对象访问:" << endl;
Son s;
s.func();
s.Base::func();
//2.通过类名访问
cout << "通过类名访问:" << endl;
Son::func();
Son::Base::func();
Son::Base::func(100);
}
int main(){test();return 0;}
6.6多继承语法
-
C++允许一个类继承多个类(认多个爹)
-
语法:
class 子类 : 继承方式 父类1, 继承方式2 父类2
-
多继承可能会引发父类中有同名成员出现,需要加作用域区分
C++实际开发中不建议使用多继承
#include<iostream>
using namespace std;
class Base1 {
public:
Base1() {
m_A = 100;
}
int m_A;
};
class Base2 {
public:
Base2() {
m_B = 200;
}
int m_B;
};
//子类继承Base1 和 Base2
class Son : public Base1, public Base2{
public:
Son()
{
m_C = 300;
m_D = 400;
}
int m_C;
int m_D;
};
void test(){
Son s;
cout << "sizeof(s) == " << sizeof(s) << endl;
}
int main(){test();return 0;}
- 总结:多继承中如果父类中出现了同名情况,子类调用时要加入作用域做区分
6.7菱形继承
-
菱形继承的概念:
1.两个派生类继承他一个基类
2.又有某个类同时继承两个派苏类
3.这种继承就被称为菱形继承,或钻石继承
- 菱形继承的典例:
-
菱形继承问题:
1.羊继承了动物数据,驼同样继承了动物数据,当草密码使用数据时,救就会产生二义性
2.草泥马继承来自动物类的两份数据,但其实我们清楚,这份数据我们只需要一份就够了
#include<iostream>
using namespace std;
class Animal//动物类
{
public:
int m_Age;
};
class Sheep : public Animal//羊类
{
public:
};
class Tuo : public Animal//驼类
{
public:
};
class SheepTuo : public Sheep, public Tuo {
public:
};
void test() {
SheepTuo st;
st.m_Age = 100;
}
int main(){test();return 0;}
- 二义性:出现菱形继承,两个父类拥有相同数据,需加以作用域区分
void test() {
SheepTuo st;
st.Sheep::m_Age = 100;
st.Tuo::m_Age = 200;
cout << "st.Sheep::m_Age == " << st.Sheep::m_Age << endl;
cout << "st.Tuo::m_Age == " << st.Tuo::m_Age << endl;
}
- 年龄这份数据,我们知道只要有一份就够了,但菱形继承却导致这份数据有两份,造成资源上的浪费
6.8虚继承 — 解决菱形继承问题
- 继承前加入关键字:
virtual ---> 变为虚继承,而其父类被称为虚基类
class Animal{
public:
int m_Age;
};
//class Sheep : virtual public Animal就是虚继承
class Sheep : virtual public Animal//此时Animal就被称为虚基类
{
public:
};
class Tuo : virtual public Animal{
public:
};
class SheepTuo : public Sheep, public Tuo {
public:
};
void test() {
SheepTuo st;
st.Sheep::m_Age = 100;
st.Tuo::m_Age = 200;
cout << "st.m_Age == " << st.m_Age << endl;
cout << "st.Sheep::m_Age == " << st.Sheep::m_Age << endl;
cout << "st.Tuo::m_Age == " << st.Tuo::m_Age << endl;
}
int main(){test();return 0;}
- m_Age被归为同一份数据,且st.m_Age也不再产生二义性(因为只有一份m_Age数据)。
总结:
- 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义的代码块
- 利用虚继承可以解决菱形继承的问题
🌟 各位看官好,我是工藤新一¹呀~
🌈 愿各位心中所想,终有所致!