学习记录——day41 继承

目录

一、继承格式

二、继承内容

三、继承方式

四、继承过程中的成员

五、类与类之间的关系模型

六、子类继承父类的过程

1、继承步骤

例1

例2

2、继承过程中的特殊成员函数

1)构造函数

2)析构函数

3)拷贝构造函数

4)拷贝赋值函数

[例3 构造函数](#例3 构造函数)

[例4 拷贝构造函数 拷贝赋值函数](#例4 拷贝构造函数 拷贝赋值函数)

七、多级继承

八、多重继承

九、菱形继承问题

十、虚继承


学习记录------day39 C++ Class this指针-CSDN博客

面向对象三大特征:封装、继承、多态

基于一个已有的类,来创建出一个新类的过程叫做继承,原类称为父类,新类称为自类/派生类。主要用于提高代码的复用性

一、继承格式

class 子类名 :继承方式 class 父类名1, class 父类2。。。

{

子类扩展的成员;

};

二、继承内容

子类会继承父类的所有成员,根据继承方式的不同,子类通国继承得到的成员权限也不同

(类中特殊的成员函数不会继承,但在对于时机会调用,用于处理被继承的成员)

学习记录------day40 类中特殊的成员函数_形参名和成员变量名相同时,使用初始化列表-CSDN博客

三、继承方式

继承方式一共有三种:public、protected、private,表示子类能够继承到的最高权限(以public为最高,private为最低)

继承方式也可以省略,默认的继承方式为 private

常用的继承方式是 public

1、类中的成员属性和成员函数分为不同的权限

public:该权限下的成员,可以在类内、子类中、类外被访问

protected:该权限下的成员,可以在类内、子类中直接被访问,类外不允许被访问

private:该权限下的成员,只能在类内被访问,子类、类外不允许被访问

2、public 继承

父类权限 public protected private 不可访问(来自父类的父类)

子类权限 public protected 不可访问 不可访问

3、protected 继承

父类权限 public protected private 不可访问(来自父类的父类)

子类权限 protected protected 不可访问 不可访问

4、private 继承

父类权限 public protected private 不可访问(来自父类的父类)

子类权限 private private 不可访问 不可访问

四、继承过程中的成员

1、子类会继承父类中的所有成员,但不能子类中不能直接使用父类的私有成员

2、子类对父类的对私有成员的操作,需要通过父类中提供public或者protected类型的接口函数完成

3、子类在实例化类对象时,会先存储父类的成员变量,再存储子类中扩展的成员变量

4、子类实例化对象时,系统会自动调用父类的构造函数,去初始化子类从父类继承的成员变量

5、在子类有参构造中调用父类的有参构造,可以初始化从父类继承成员变量

学习记录------day40 类中特殊的成员函数_形参名和成员变量名相同时,使用初始化列表-CSDN博客

6、子类实例化对象,虽然调用的父类的构造函数,但是并没有实例化父类对象,最终对象的个数只有一个

五、类与类之间的关系模型

1、继承关系:is a 模型,是特殊的包含关系(has a模型)

2、包含关系:has a模型,在一个类中,有另一个类的成员子对象

3、友元关系:use a模型,在一个类中,使用另一类中的内容

六、子类继承父类的过程

1、继承步骤

1)完全接收父类的成员

2)根据继承类型更改父类中的成员属性(系统),后续也可通过 using 关键字 更改可访问的成员权限(编程者) 例1

3)拓展新成员(由编程者自己定义,可无)。新成员可和继承来的成员同名,它们存储在不同的地址 例2

例1

class Father
{
public:
    int num;
protected:
    int value;
private:
    int key;
    
public:
    Father(){}
    Father(int n,int v,int k):num(n),value(v),key(k){}
    ~Father(){}
};


//继承
class Son:public Father
{
//子类可以更改从父类继承来的,能访问成员的权限、内容
public:  
    //把继承下来的 protected: value 的权限改为 public
    using Father::value; 
};

例2

#include <iostream>

using namespace std;

class Father
{
public:
    int num;
protected:
    int value;
private:
    int key;
    
public:
    Father(){}
    Father(int n,int v,int k):num(n),value(v),key(k){}
    ~Father(){}
    
    void show()
    {
        cout<<"Father"<<endl;
        cout<<num<<value<<key<<endl;
        cout<<"***********"<<endl;
    }
};


//继承
class Son:public Father
{
//子类可以更改从父类继承来的,能访问成员的权限、内容
public:  
    //把继承下来的 protected: value 的权限改为 public
    using Father::value; 
    string name;//子类自己扩展的

public: 
    Son(){}
    Son(int n,int v,int k,string name):Father(n,v,k),name(name){}
    
    //与父类同名的函数  通过子类对象调用 show 函数时 调用的时子类的
    void show()
    {
        cout<<"son"<<endl;
        cout<<num<<value<<endl;
        cout<<"***********"<<endl;
    }
};

int main()
{
    Son s(5,5,5,"faiz");
    s.show();//默认调用自己的show
    s.Father::show();//调用 父类的 show 
                     //对于同名成员变量调用也相同
    return 0;
}

2、继承过程中的特殊成员函数

1)构造函数

子类实例化对象时,系统会先调用父类的构造函数处理子类中继承下来的成员,再调用子类自己的构造函数处理子类自己的成员。对于继承下来的成员,系统默认调用无参构造函数,调用有参构造需要再定义子类的构造函数时显性调用父类的有参构造。 例3

2)析构函数

析构子类时,系统会先调用父类的析构函数,来完成对子类从父类中继承下来成员的空间回收,再调用子类自己的析构函数,对子类自己的成员的空间进行回收

3)拷贝构造函数

父子类中拥有不同的拷贝构造函数,需要在子类的拷贝构造函数的初始化列表中显性调用父类的拷贝构造函数来完成对子类从父类继承下来成员的初始化工作,如果没有显性调用父类的拷贝构造函数,那么系统会自动调用父类的无参构造来完成对那些成员的初始化工作

例4

4)拷贝赋值函数

父子类中拥有不同的拷贝赋值函数,需要在子类的拷贝赋值函数的函数体内,显性调用父类的拷贝赋值函数来完成对子类从父类中继承的成员的赋值工作,如果没有显性调用父类的拷贝赋值函数,那么系统不会自动调用其他函数。当前对象中父类从子类中继承下来的成员的值保持不变

例4

例3 [构造函数](#例3 构造函数)

#include <iostream>

using namespace std;

class Father
{
public:
    int num;
protected:
    int value;
private:
    int key;

public:
    Father(){cout<<"F 无参构造"<<endl;}
    Father(int n,int v,int k):num(n),value(v),key(k){cout<<"F 有参构造"<<endl;}
    ~Father(){cout<<"F 析构函数"<<endl;}

    void show()
    {
        cout<<"Father"<<endl;
        cout<<num<<value<<key<<endl;
        cout<<"***********"<<endl;
    }
};


//继承
class Son:public Father
{
//子类可以更改从父类继承来的,能访问成员的权限、内容
public:
    //把继承下来的 protected: value 的权限改为 public
    using Father::value;//仅仅是声明 更改权限 value 调用时 仍需 Father::
    string name;//子类自己扩展的

public:
    Son(){cout<<"S 无参构造"<<endl;}
    Son(int n,int v,int k,string name):Father(n,v,k),name(name){cout<<"S 有参构造"<<endl;}
    ~Son(){cout<<"S 析构函数"<<endl;}

    //与父类同名的函数  通过子类对象调用 show 函数时 调用的时子类的
    void show()
    {
        cout<<"son"<<endl;
        cout<<num<<value<<endl;
        cout<<"***********"<<endl;
    }
};

int main()
{
    Son s(5,5,5,"faiz");
    s.show();//默认调用自己的show
    s.Father::show();//调用 父类的 show

    //对于同名成员变量调用也相同
    cout<<"*************"<<endl;
    cout<<s.Father::num<<endl;

    return 0;
}

例4 [拷贝构造函数](#例4 拷贝构造函数 拷贝赋值函数)[拷贝赋值函数](#例4 拷贝构造函数 拷贝赋值函数)

#include <iostream>

using namespace std;

class Father
{
public:
    int num;
protected:
    int value;
private:
    int key;

public:
    Father(){cout<<"F 无参构造"<<endl;}
    Father(int n,int v,int k):num(n),value(v),key(k){cout<<"F 有参构造"<<endl;}
    ~Father(){cout<<"F 析构函数"<<endl;}
    Father(const Father& other):num(other.num),value(other.value),key(other.key){cout<<"F 拷贝构造"<<endl;}
    Father& operator=(const Father&other)
    {
        if(this != &other)
        {
            this->num = other.num;
            this->key = other.key;
            this->value = other.value;
        }
        cout<<"F 拷贝赋值"<<endl;

        return *this;
    }

    void show()
    {
        cout<<"Father"<<endl;
        cout<<num<<value<<key<<endl;
    }
};


//继承
class Son:public Father
{
//子类可以更改从父类继承来的,能访问成员的权限、内容
public:
    //把继承下来的 protected: value 的权限改为 public
    using Father::value;
    string name;//子类自己扩展的

public:
    Son(){cout<<"S 无参构造"<<endl;}
    Son(int n,int v,int k,string name):Father(n,v,k),name(name){cout<<"S 有参构造"<<endl;}
    ~Son(){cout<<"S 析构函数"<<endl;}
    Son(const Son& other):Father(other){cout<<"S 拷贝构造"<<endl;}
    //调用子类的拷贝赋值函数
    Son& operator=(const Son& other)
    {
        if(this != &other)
        {
            //显性调用父类的拷贝赋值函数
            this->Father::operator=(other);
            this->name = other.name;
        }
        cout<<"S 拷贝赋值"<<endl;
        return *this;
    }

    //与父类同名的函数  通过子类对象调用 show 函数时 调用的时子类的
    void show()
    {
        cout<<"son"<<endl;
        cout<<num<<value<<name<<endl;
    }
};

int main()
{
    cout<<"******拷贝赋值*******"<<endl;
    Son s1(5,5,5,"faiz");
    Son s2 = s1;//拷贝构造
    cout<<"s2 ";
    s2.show();

    cout<<"s1 ";
    s1.show();

    cout<<"******拷贝赋值*******"<<endl;
    //拷贝赋值
    Son s3;
    s3 = s2;
    cout<<"s3 ";
    s2.show();

    cout<<"********************"<<endl;

    return 0;
}

七、多级继承

1) C++中允许多级继承,子类继承自父类,孙子类继承自子类

2)当前子类会用于祖先类的所有成员,包括私有成员

八、多重继承

1)C++的面向对象是支持多重继承的

2)允许一个子类由多个父类共同派生出来,当前子类会继承所有父类的成员

3)继承格式

class 子类名:继承方式1 父类1, 继承方式2 父类2,。。。,继承方式n 父类n

{

子类拓展成员

}

4)需要在子类的构造函数的初始化列表中,显性调用所有父类的构造函数来完成对从不同父类中继承下来成员的初始化工作,如果没有显性调用有参构造,系统会自动调用对应父类的无参构造完成初始化工作

5)子类中调用父类的构造函数的顺序跟继承顺序有关,跟构造函数的摆放顺序无关

6)产生问题:多个父类中可能会出现同名的成员,子类对象访问起来就会产生歧义。

解决办法:需要使用对应的父类名和作用域限定符来指定

九、菱形继承问题

公共基类

/ \

中间子类 中间子类

\ /

汇聚子类

中间子类会正常继承公共基类的所有成员,但是,使用多个中间子类共同派生出一个汇聚子类时,该汇聚子类中,就会同时拥有多份公共基类的成员,会造成汇聚子类类体膨胀,访问数据也比较麻烦。这个问题成为菱形基础问题也称钻石继承。

十、虚继承

1)虚继承是为了解决菱形继承问题引入的

2)继承格式:在生成中间子类时,在继承方式前加关键字 virtual ,那么该继承方式就是虚继承

3)汇聚子类中,仅仅只保留一份公共基类的成员

4)由于传递给公共基类的数据只有一份,但是有多个父类的构造函数。原则上来说,从父类中继承的成员需要调用直接父类来进行初始化操作,但是这一份成员,不能确定是由哪一个父类来进行构造。索性,直接父类的构造函数全部都不用,直接由公共基类进行构造。

5)需要在汇聚子类的构造函数初始化列表中,显性调用公共基类的有参构造,来完成对汇聚子类中从公共基类中继承的成员的初始化工作,如果没有显性调用公共基类的有参构造,则系统会自动调用公共基类的无参构造来完成。

相关推荐
狂飙的张兴发12 分钟前
认知小文2《成功之路:习惯、学习与实践》
学习·考研·职场和发展·跳槽·学习方法·改行学it·高考
爱编程的小新☆16 分钟前
C语言内存函数
c语言·开发语言·学习
夜清寒风1 小时前
opencv学习:图像掩码处理和直方图分析及完整代码
人工智能·opencv·学习·算法·机器学习·计算机视觉
吃着火锅x唱着歌2 小时前
Go语言设计与实现 学习笔记 第七章 内存管理(1)
笔记·学习·golang
我命由我123453 小时前
2.使用 VSCode 过程中的英语积累 - Edit 菜单(每一次重点积累 5 个单词)
前端·javascript·ide·vscode·学习·编辑器·学习方法
Pluses3 小时前
Datawhale X 李宏毅苹果书 AI夏令营 《深度学习详解》第十九章 ChatGPT
人工智能·笔记·深度学习·学习
南山933 小时前
如何快速学习拼音打字?
学习·大学生·打字侠
2401_856926934 小时前
图片转PDF技巧揭秘:四款高效工具推荐!
学习·pdf·图片转pdf·图片转pdf工具
Austim小白5 小时前
QT消息对话框学习
qt·学习
kuiini5 小时前
python学习-09【文件和目录操作】
python·学习