• c++进阶(c++里的继承)


    1.继承的概念及定义

    1.1继承的概念

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

    我们先写一个继承类来了解一下吧。

    class person  //父类(基类)
    {
    public:
    	void print()
    	{
    		cout << _name << '/' << _age << endl;
    	}
    protected:
    	string _name = "Yui";
    	int _age = 17;
    };
    class student :public person//子类(派生类)
    {
    protected:
    	int stu_id;
    };
    class teacher :public person
    {
    protected:
    	int tea_id;
    };
    
    int main()
    {
    	student s;
    	teacher t;
    	s.print();
    	t.print()
    	
    	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

    对于这代码,student和teacher都继承于person。所以说,person为父类(基类)student和teacher为子类(派生类)。子类(派生类)会继承父类(基类)的成员(成员变量和成员函数)。
    我们可以通过vs的监视窗口来观察
    监视窗口
    可以看到派生类里面不仅有自己的成员还存在基类的成员。

    1.2继承的定义

    1.2.1定义格式

    我们可以看到,student 为派生类,public为继承方式,person为基类

        //派生类   继承方式  基类
    class student :public person//子类(派生类)
    {
    protected:
    	int stu_id;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1.2.2继承关系和访问限定符

    继承关系和访问限定符

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

    类成员/继承方式public继承protected继承private继承
    基类的public成员派生类的public成员派生类的protected成员派生类的private成员
    基类的protected成员派生类的protected成员派生类的protected成员派生类的private成员
    基类的private成员在派生类中不可见在派生类中不可见在派生类中不可见

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

    实际写就是这样

    class person  //父类(基类)
    {
    public:
    	void print()
    	{
    		cout << _name << '/' << _age << endl;
    	}
    protected:
    	string _name = "Yui";
    private:
    	int _age = 17;
    };
    //class student :protected person//子类(派生类)
    //class student :private person//子类(派生类)
    class student :public person//子类(派生类)
    {
    protected:
    	int stu_id;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    2.基类和派生类对象赋值转化

    派生类对象可以赋值给基类的对象/基类的指针/基类的引用。这里有个形象的说法叫做切片或者切割。意思是吧派生类里父类那部分给切割过去。
    基类对象不能赋值给派生类对象
    基类的指针或者引用可以通过强制类型换赋值给派生类的指针或者引用,但是必须是基类的指针指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用RTTI(Run-TIme
    Type information)的dynamic_cast来识别后进行安全转换
    切割

    class person
    {
    public:
    	void print()
    	{
    		cout << _name << '/' << _age << endl;
    	}
    protected:
    	string _name = "Yui";
    	int _age = 17;
    };
    class student :public person
    {
    protected:
    	int stu_id;
    };
    class teacher :public person
    {
    protected:
    	int tea_id;
    };
    
    int main()
    {
    	//父类不能给子类赋值,但是可以对父类进行强制类型转化来进行(必须是指针转化)
    	//子类可以对父类赋值,父类会对子类的成员进行切割
    	student st;
    	person p = st;
    	person* pp = &st;//指针
    	person& ppp = st;//引用
    	student sst = p;//编译器报错
    	student* stt = (student*)pp;//
    	student sttt = (student)p;//编译器报错
    	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

    3.继承中的作用域

    1.在继承体系中基类和派生类都要独立的作用域。
    2.子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也可以叫重定义。(在子类成员函数中,可以使用基类::基类成员显示访问)
    3.需要注意的是如果成员函数的隐藏,只需要函数名相同就构成隐藏
    4.只需要在实际中在继承体现里最好不要定义同名成员

    class Person
    {
    protected:
    	string _name = "Yui"; // 姓名
    	int _num = 111;// 身份证号
    };
    class Student : public Person
    {
    public:
    	void Print()
    	{
    		cout << " 姓名:" << _name << endl;
    		cout << " 身份证号:" << Person::_num << endl;
    		cout << " 学号:" << _num << endl;
    	}
    protected:
    	int _num = 999; // 学号
    };
    void Test1()
    {
    	Student s1;
    	s1.Print();
    };
    // B中的fun和A中的fun不是构成重载,因为不是在同一作用域
    // B中的fun和A中的fun构成隐藏,成员函数满足函数名相同就构成隐藏。
    class A
    {
    public:
    	void fun()
    	{
    		cout << "func()" << endl;
    	}
    };
    class B : public A
    {
    public:
    	void fun(int i)
    	{
    		A::fun();
    		cout << "func(int i)->" << i << endl;
    	}
    };
    void Test2()
    {
    	B b;
    	b.fun(10);
    };
    
    int main()
    {
    	Test1();
    	Test2();
    	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

    4.派生类的默认成员函数

    6个默认成员函数,“默认”的意思就是为即使我们不写,编译器也会给我门自动生成有个,那么在派生类中,这几个成员是如何生成的呢?

    1.派生类的构造函数必须调用基类的构造函数初始化基类的那部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
    2.派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝构造。
    3.派生类的operator=必须要调用基类的operator=完成基类的复制
    4.派生类的析构函数会在调用完成后自动调用基类的析构函数清理基类成员。因为这样才可能保证派生类对象先清理派生类成员再清理基类成员的顺序
    5.派生类对象初始化先调用基类构造再调用派生类构造
    6.派生类对象析构清理先调用派生类析构再调基类的析构

    在这里插入图片描述
    执行顺序

    5.继承与友元

    友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员
    (可以理解为父亲的朋友不一定是你的朋友。)

    6.继承域静态成员

    基类定义了static静态成员,则整个继承体现里只有这一个这样的成员。物理派生出来了多少个子类,都只有一个static成员实例。

    class Person
    {
    public:
    	Person() { ++_count; }
    protected:
    	string _name; // 姓名
    public:
    	static int _count; // 统计人的个数。
    };
    int Person::_count = 0;
    class Student : public Person
    {
    protected:
    	int _stuNum; // 学号
    };
    class Graduate : public Student
    {
    protected:
    	string _seminarCourse; // 研究科目
    };
    void TestPerson()
    {
    	Student s1;
    	Student s2;
    	Student s3;
    	Graduate s4;
    	cout << " 人数 :" << Person::_count << endl;
    	Student::_count = 0;
    	cout << " 人数 :" << Person::_count << endl;
    }
    int main()
    {
    	TestPerson();
    	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

    结果
    结果

    在这里插入图片描述

  • 相关阅读:
    PHP禁止单个用户多设备同时登陆,限制单个用户在多端重复登录
    【办公类-04-04】华为助手导出照片视频分类(根据图片、视频的文件名日期导入“年-月-日”文件夹中,并转移到“年-月”文件中整理、转移到“年”文件夹中整理)
    MATLAB:电机控制(Motor Control)
    5G网络架构与组网部署01--5G网络架构的演进趋势
    LoadRunner VUG 脚本
    海滨体育馆管理系统/基于Spring Boot的海滨体育馆管理系统设计(源码+数据库+文档)
    现货黄金代理好吗?
    都知道0.1+0.2 = 0.30000000000000004,那要怎么让它等于0.3
    LabVIEW通信-CAN
    索引优化分析_预热_JOIN
  • 原文地址:https://blog.csdn.net/2303_79015671/article/details/136692897