• 【一起来学C++】————(6)C++中的继承


    1.继承的概念及定义

    1.1继承的概念

      继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,
    体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用

    有些类与类之间存在特殊的关系,例如下图中:

    在这里插入图片描述

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

    1.2 继承定义

    1.2.1定义格式

    class A : public B

    A 类称为子类 或 派生类
    B 类称为父类 或 基类

    派生类中的成员,包含两大部分:

    • 一类是从基类继承过来的,
    • 一类是自己增加的成员。

      从基类继承过过来的表现其共性,而新增的成员体现了其个性。

    1.2.2 继承方式

    继承的语法: class 子类 : 继承方式 父类
    继承方式一共有三种:

    ● 公共继承
    ● 保护继承
    ● 私有继承

    继承关系和访问限定符区别:
    在这里插入图片描述

    1.2.3继承基类成员访问方式的变化

    在这里插入图片描述
    总结:

    1. 基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见(相当于“隐形遗传”,继承到了只是编译器对外进行隐藏了)是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。
    2. 基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的。
    3. 实际上面的表格我们进行一下总结会发现,基类的私有成员在子类都是不可见。基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),public > protected > private。
    4. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式。
    5. 在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强。

    在这里插入图片描述

    1.3继承中构造和析构顺序

    子类继承父类后,当创建子类对象,也会调用父类的构造函数

    问题:父类和子类的构造和析构顺序是谁先谁后?

    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()
    {
    	//继承中 先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反
    	Son s;
    }
    
    int main() {
    
    	test01();
    
    	system("pause");
    
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    总结:继承中 先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反

    1.4继承同名成员处理方式于静态变量

    问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢?

    ● 访问子类同名成员 直接访问即可
    ● 访问父类同名成员 需要加作用域

    静态变量作用范围在一个文件内,程序开始时分配空间,结束时释放空间,默认初始化为0,使用时可以改变其值。
    静态变量或静态函数只有本文件内的代码才能访问它,它的名字在其它文件中不可见。

    // 类内声明,类外初始化,全局区开辟空间
    class Base {
    public:
    	static void func()
    	{
    		cout << "Base - static void func()" << endl;
    	}
    	static void func(int a)
    	{
    		cout << "Base - static void func(int a)" << endl;
    	}
    
    	static int m_A;// 类内声明
    };
    
    int Base::m_A = 100;//类外初始化,全局区开辟空间
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    示例:

    class Base {
    public:
    	static void func()
    	{
    		cout << "Base - static void func()" << endl;
    	}
    	static void func(int a)
    	{
    		cout << "Base - static void func(int a)" << endl;
    	}
    
    	static int m_A;// 类内声明,类外初始化,全局区开辟空间
    };
    
    int Base::m_A = 100;//
    
    class Son : public Base {
    public:
    	static void func()
    	{
    		cout << "Son - static void func()" << endl;
    	}
    	static int m_A;
    };
    
    int Son::m_A = 200;
    
    //同名成员属性
    void test01()
    {
    	//通过对象访问
    	cout << "通过对象访问: " << endl;
    	Son s;
    	cout << "Son  下 m_A = " << s.m_A << endl;
    	cout << "Base 下 m_A = " << s.Base::m_A << endl;
    
    	//通过类名访问
    	cout << "通过类名访问: " << endl;
    	cout << "Son  下 m_A = " << Son::m_A << endl;
    	cout << "Base 下 m_A = " << Son::Base::m_A << endl;
    }
    
    //同名成员函数
    void test02()
    {
    	//通过对象访问
    	cout << "通过对象访问: " << endl;
    	Son s;
    	s.func();
    	s.Base::func();
    
    	cout << "通过类名访问: " << endl;
    	Son::func();
    	Son::Base::func();
    	//出现同名,子类会隐藏掉父类中所有同名成员函数,需要加作作用域访问
    	Son::Base::func(100);
    }
    int main() {
    
    	//test01();
    	test02();
    
    	system("pause");
    
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    总结:同名静态成员处理方式和非静态处理方式一样,只不过有两种访问的方式(通过对象通过类名

    1.6多继承语法

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

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

    多继承可能会引发父类中有同名成员出现,需要加作用域区分

    C++实际开发中不建议用多继承
    
    • 1

    示例:

    class Base1 {
    public:
    	Base1()
    	{
    		m_A = 100;
    	}
    public:
    	int m_A;
    };
    
    class Base2 {
    public:
    	Base2()
    	{
    		m_A = 200;  //开始是m_B 不会出问题,但是改为mA就会出现不明确
    	}
    public:
    	int m_A;
    };
    
    //语法:class 子类:继承方式 父类1 ,继承方式 父类2 
    class Son : public Base2, public Base1 
    {
    public:
    	Son()
    	{
    		m_C = 300;
    		m_D = 400;
    	}
    public:
    	int m_C;
    	int m_D;
    };
    
    
    //多继承容易产生成员同名的情况
    //通过使用类名作用域可以区分调用哪一个基类的成员
    void test01()
    {
    	Son s;
    	cout << "sizeof Son = " << sizeof(s) << endl;
    	cout << s.Base1::m_A << endl;
    	cout << s.Base2::m_A << endl;
    }
    
    int main() {
    
    	test01();
    
    	system("pause");
    
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

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

    1.7菱形继承

    菱形继承概念:

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

    典型的菱形继承案例:

    在这里插入图片描述
    菱形继承问题:

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

    示例:

    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 = 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;
    	cout << "st.m_Age = " << st.m_Age << endl;
    }
    
    
    int main() {
    
    	test01();
    
    	system("pause");
    
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    总结:
    ● 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义
    ● 利用虚继承可以解决菱形继承问题
    继承前加virtual关键字后,变为虚继承
    此时公共的父类称为虚基类

  • 相关阅读:
    【背包问题】基于matlab遗传算法结合贪婪算法求解背包问题【含Matlab源码 791期】
    浅谈青岛啤酒厂事件—论智能视频监控的重要性和必要性
    hdu7244-Winner Prediction(22多校第十场1001 dinic最大流)
    重建二叉树 -- 结合前中后序列
    地下水除砷工艺
    黑马JVM总结(二十八)
    Docker快速搭建漏洞靶场指南
    spring cloud gateway谓词工厂 Predicate Factory
    Day 46 | 139.单词拆分 & 多重背包理论基础 & 背包问题总结
    Java ArrayList与顺序表
  • 原文地址:https://blog.csdn.net/qq_38364548/article/details/126083836