学习记录——day41 继承

目录

一、继承格式

二、继承内容

三、继承方式

四、继承过程中的成员

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

六、子类继承父类的过程

1、继承步骤

  例1

例2

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

1)构造函数

2)析构函数

3)拷贝构造函数 

4)拷贝赋值函数

例3  构造函数 

 例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  构造函数 

#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 拷贝构造函数 拷贝赋值函数

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

  • 13
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值