C++继承与派生


一、C++继承与派生

1.C++继承和派生实例讲解

继承与派生
比如有两个类,新类拥有原有类的全部属性叫做继承!原有类产生新类的过程叫做派生!而我们把原有的这个类称之为父类或基类,由基类派生出的类叫做派生类或者叫做子类。大家从名字上就可以看出他们的关系。

那么继承和派生有什么好处呢?为什么C++要有这种机制呢?

  1. 体现面向对象的编程思想,更好的表达各类型之间的关系。
  2. 派生类除了可以继承基类的全部信息外,还可以添加自己的那些不同的、有差异的信息,就像生物进化的道理一样,派生类在拥有基类的全部基础之上还将更强大。
  3. 派生类继承到基类的成员是自动、隐藏的拥有,即不需要我们重新定义,这就节省了大量的代码,体现了代码重用的软件工程思想。

实际代码的实现,如何继承,实现派生类:

#include<iostream>
using namespace std;
class Clock
{
private:
    int H;
    int M;
    int S;
public:
    int SetTime(int h,int m,int s)
    {
        this->H = h;
        this->M = m;
        this->S = s;
        return 0;
    }
    int ShowTime()
    {
        cout<<"Now:"<<H<<":"<<M<<":"<<S<<endl;
        return 0;
    }
 
};
 
class AlarmClock:public Clock
{
private:
    int AH;
    int AM;
public:
    int SetAlarm(int AH,int AM)
    {
        this->AH = AH;
        this->AM = AM;
        return 0;
    }
    int ShowAlarm()
    {
        cout<<"AlarmTime:"<<AH<<":"<<AM<<endl;
        return 0;
    }
};
 
int main()
{
    AlarmClock A;
    A.SetTime(19,15,50);
    A.ShowTime();
    A.SetAlarm(5,30);
    A.ShowAlarm();
    return 0;
}

在这里插入图片描述

其中Clock为基类,AlarmClock为派生类,注意定义派生类的语句:

class AlarmClock:public Clock

通过冒号表示继承,其中public表示公有继承、私有继承和保护继承,
定义了AlarmClock这个派生类的对象A,然后可以调用来自基类的SetTime和ShowTime方法,使用起来自己的一样。同时,我们又在派生类中增加定义了自己的SetAlarm和ShowAlarm方法。

2.C++三种继承方式实例详解

公有继承、私有继承、保护继承。

不同的继承方式,主要区别在于基类中不同访问权限的成员在派生类中的访问权限变化情况。

  1. 公有继承:
    在公有继承的模式下,其特点如下:
    (1)基类中的公有成员,在派生类中仍然为公有成员,当然无论派生里的成员函数还是派生类对象都可以访问。
    (2)基类中的私有成员,无论在派生类的成员还是派生类对象都不可以访问。
    (3)基类中的保护成员,在派生类中仍然是保护类型,可以通过派生类的成员函数访问,但派生类对象不可以访问。
  1. 私有继承:
    在私有继承的情况下,公有类型、私有类型、受保护类型三种成员的访问权限如下:
    (1)基类的公有和受保护类型,被派生类私有继承吸收后,都变为派生类的私有类型,即在类的成员函数里可以访问,不能在类外访问。
    (2)而基类的私有成员,在派生类无论类内还是类外都不可以访问。
    我们可以看出来,如果为私有派生,则基类的私有成员在派生类甚至再派生出的子类中,都无法再使用,没有什么存在意义,故这种使用情况比较少。
  1. 保护继承:
    保护类型的继承,特点如下:
    (1)基类的公有成员和保护类型成员在派生类中为保护成员。
    (2)基类的私有成员在派生类中不能被直接访问。
    可以看的出来,派生类里的成员函数可以访问基类的公有成员和保护成员,但在类外通过派生类对象则无法访问它们。同样,无论派生类里的成员函数还是通过类对象都无法访问基类中的私有成员。

但对于私有成员,我们试图增加一行代码见41行。在派生类的成员函数中对吸收来自基类的H变量进行赋值,编译得到报错,见红色方框与下方提示
在这里插入图片描述
而对于受保护类型的,我们尝试在基类中定义一个protected类型的变量w,并试图在公有继承的派生类方法中赋值使用,可以看到下图的情况,完全可以访问。如下图:
在这里插入图片描述
对于私有继承,公开类型的成员在类外访问情况:
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

3.C++派生类的构造函数实例详解

由于派生类包含基类的原因,我们在创建一个派生类的时候,系统会先创建一个基类。需要注意的是,派生类会吸纳基类的全部成员,但并不包括构造函数及后面讲的析构函数,那么就意味着创建派生类在调用自己的构造函数之前,会先调用基类的构造函数。

通过代码验证:

#include<iostream>
using namespace std;
class Clock
{
private:
    int H;
    int M;
    int S;
public:
    Clock()
    {
        cout<<"Clock's Constructor Called!"<<endl;
    }
  
};
  
class AlarmClock:public Clock
{
private:
    int AH;
    int AM;
public:
    AlarmClock()
    {
        cout<<"AlarmClock's Constructor Called!"<<endl;
    }
  
};
  
int main()
{
    AlarmClock A;
  
    return 0;
}

在这里插入图片描述

我们可以看到仅仅定义了一个派生类对象,派生类和基类的构造函数会自动调用,调用顺序是先调用基类的构造函数再调用派生类的构造函数。

以上大家看到的是最常见也最简单的调用方法,这仅仅是隐式的,也就是不用写出来的、自动的调用。那么当基类的构造函数是带参数的情况下如何调用呢?这样还可以吗?如何传参呢?
答:那就需要我们显式的,也就是明确的写出来,并指定参数传递,来告诉编译器。

一般的写法格式为:
派生类构造函数名(总形参表列):基类构造函数(实参表列)

#include<iostream>
using namespace std;
class Clock
{
private:
    int H;
    int M;
    int S;
public:
    Clock()
    {
        cout<<"Clock's Constructor Called!"<<endl;
    }
    Clock(int h,int m,int s)
    {
        this->H = h;
        this->M = m;
        this->S = s;
        cout<<"Clock's Constructor  with  parameter Called!"<<endl;
    }
  
};
class AlarmClock:public Clock
{
private:
    int AH;
    int AM;
public:
    AlarmClock()
    {
        cout<<"AlarmClock's Constructor Called!"<<endl;
    }
    AlarmClock(int h,int m,int s):Clock(h,m,s)
    {
        cout<<"AlarmClock's Constructor  with  parameter Called!"<<endl;
    }
};
int main()
{
    AlarmClock A(8,10,30);
    AlarmClock B;
    return 0;
}

在这里插入图片描述

大家注意看派生类的构造函数,后面通过冒号跟基类的传参,且基类里的参数里为实参,来实现显示的参数调用。

需要注意的是,一旦基类中有带参数的构造函数,派生类中则必须有显式传参的派生类构造函数,来实现基类中参数的传递,完成初始化工作。

4.C++派生类的析构函数实例详解

在派生类中,析构函数也无法被派生类吸收。
重点大家需要清楚派生类和基类的析构函数的调用顺序,析构函数的调用顺序与构造函数则完全相反,我们可以在派生类析构函数和基类析构函数中输出一条信息,观察调用顺序:

#include<iostream>
using namespace std;
class Clock
{
private:
    int H;
    int M;
    int S;
public:
    Clock()
    {
        cout<<"Clock's Constructor Called!"<<endl;
    }
    ~Clock()
    {
        cout<<"Clock's Destructor Called!"<<endl;
    }
  
};
  
class AlarmClock:public Clock
{
private:
    int AH;
    int AM;
public:
    AlarmClock()
    {
        cout<<"AlarmClock's Constructor Called!"<<endl;
    }
    ~AlarmClock()
    {
        cout<<"AlarmClock's Destructor Called!"<<endl;
    }
  
};
  
int main()
{
    AlarmClock A;
  
    return 0;
}

在这里插入图片描述

构造函数调用顺序:基类->派生类
析构函数调用顺序:派生类->基类

5.C++虚基类及虚基类的定义使用

虚基类
在多继承关系中,如果一个派生类的从两个父类那里继承过来,并且这两个父类又恰恰是从一个基类那里继承而来。那这样就麻烦了,因为你可能继承了两份一样的成员!

虚基类!所谓虚基类就是在继承的时候在继承类型public之前用virtual修饰一下,比如还是这个例子,只需要父亲类在继承爷爷类的时候多加一个virtual,那么这个时候,派生类和基类就只维护一份一个基类对象。避免多次拷贝,出现歧义。

定义方法即在两个父亲类的派生时增加virtual的声明:

class Father1:virtual public Grandfather
class Father2:virtual public Grandfather

总结

与人为善,与万物为善。
来自“https://www.dotcpp.com/course”总结

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值