C++<第二十一篇>:继承

继承是面向对象的主要特征之一,它使得一个类可以从现有类中派生,而不必重新定义一个新类。
继承的实质就是用已有数据类型创建新的数据类型,并保留已有数据类型的特点,以旧类为基础创建新类,新类包含了旧类的成员变量和成员函数,并且可以在新类中添加新的成员变量和成员函数。
旧类被称为基类或者父类,新类被称为派生类或者子类。

(1)类继承的一般格式
class 派生类名: [继承方式] 基类名
{
    成员...
}

派生类又称子类,基类又称父类,所以以下的格式和前者是一致的:

class 子类类名: [继承方式] 父类名
{
    成员...
}

继承方式有三种,分别是公有型(public)、保护型(protected)和私有型(private)。

(2)继承的使用

假设有一个 Pesonal(人) 对象,Pesonal有姓名和年龄两个属性:

class Pesonal 
{
private:
    string mName; // 姓名
    int mAge; // 年龄

public:
    void setName(string name) 
    {
        mName = name;
    }

    string getName() 
    {
        return mName;
    }

    void setAge(int age) 
    {
        mAge = age;
    }

    int getAge() 
    {
        return mAge;
    }
};

还有一个Student(学生)对象,Student有姓名、年龄、成绩三个属性:

class Student
{
private:
    string mName; // 姓名
    int mAge; // 年龄
    int16_t mScore; // 成绩

public:
    void setName(string name)
    {
        mName = name;
    }

    string getName()
    {
        return mName;
    }

    void setAge(int age)
    {
        mAge = age;
    }

    int getAge()
    {
        return mAge;
    }

    void setScore(int score) 
    {
        mScore = score;
    }

    int getScore() 
    {
        return mScore;
    }
};

学生也是人,因此学生也有人的特性,Student也具有Pesonal的姓名和年龄两种属性,所以,这两个类可以使用继承关系。
我们需要对Student类做下修改,代码如下:

class Student:public Pesonal
{
private:
    int16_t mScore; // 成绩

public:

    void setScore(int score) 
    {
        mScore = score;
    }

    int getScore() 
    {
        return mScore;
    }
};

Student类中,只要添加Student特有的属性即可,Student可以使用Pesonal中的属性。

使用代码如下:

Student student;
student.setName("zhangsan");
student.setAge(16);
student.setScore(90);

cout << "姓名:" << student.getName() << endl;
cout << "年龄:" << student.getAge() << endl;
cout << "成绩:" << student.getScore() << endl;
(2)访问控制和继承

派生类可以访问基类中所有的非私有成员。因此基类成员如果不想被派生类的成员函数访问,则应在基类中声明为 private。

我们可以根据访问权限总结出不同的访问类型,如下所示:

访问 public protected private
同一个类 yes yes yes
派生类(子类) yes yes no
外部的类 yes no no

一个派生类继承了所有的基类方法,但下列情况除外:

  • 基类的构造函数、析构函数和拷贝构造函数。
  • 基类的重载运算符。
  • 基类的友元函数。
  • 继承方式
(3)继承方式

继承方式有 public、private、protected 3种,3种继承方式说明如下:

我们几乎不使用 protected 或 private 继承,通常使用 public 继承。当使用不同类型的继承时,遵循以下几个规则:

  • 公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
  • 保护继承(protected): 当一个类派生自保护基类时,基类的公有和保护成员将成为派生类的保护成员。
  • 私有继承(private):当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员。
(4)构造函数和析构函数的访问顺序

为了方便研究派生类和基类构造函数和析构函数的调用顺序,需要写一个演示代码:

#include <iostream>
#include <string>

using namespace std;

class Pesonal 
{
private:
    string mName; // 姓名
    int mAge; // 年龄

public:
    Pesonal() 
    {
        cout << "构造函数 --- Pesonal" << endl;
    }
    ~Pesonal() 
    {
        cout << "析构函数 --- Pesonal" << endl;
    }

    void setName(string name) 
    {
        mName = name;
    }

    string getName() 
    {
        return mName;
    }

    void setAge(int age) 
    {
        mAge = age;
    }

    int getAge() 
    {
        return mAge;
    }
};

class Student:public Pesonal
{
private:
    int16_t mScore; // 成绩

public:
    Student()
    {
        cout << "构造函数 --- Student" << endl;
    }
    ~Student()
    {
        cout << "析构函数 --- Student" << endl;
    }
    void setScore(int score) 
    {
        mScore = score;
    }

    int getScore() 
    {
        return mScore;
    }
};

int main()
{
    Student student;
    student.setName("zhangsan");
    student.setAge(16);
    student.setScore(90);

    cout << "姓名:" << student.getName() << endl;
    cout << "年龄:" << student.getAge() << endl;
    cout << "成绩:" << student.getScore() << endl;

    return 0;
}

在运用时,我们只创建了派生类对象,调用后的输出结果是:

构造函数 --- Pesonal
构造函数 --- Student
姓名:zhangsan
年龄:16
成绩:90
析构函数 --- Student
析构函数 --- Pesonal

所以,构造函数的调用顺序是:

基类(父类)  -->     派生类(子类)

析构函数的调用顺序和构造函数相反:

派生类(子类)  -->   基类(父类)
(5)子类显式调用父类的构造函数

当子类实例化时,子类和父类的构造函数都会被执行;
子类执行哪个构造函数取决于子类实例化传入的参数;
那么,到底执行了父类的哪个构造函数呢?

一般情况下,默认调用父类没有形式参数的构造方法,如果想要调用父类有形式参数的构造方法,我们需要显式调用父类的构造函数,代码如下:

Pesonal类的带参构造方法:

Pesonal(string name, int age)
{
    mName = name;
    mAge = age;
}

Student类的无参构造方法:

Student() :Pesonal("zhangsan", 16)
{
    
}

此时,先调用Pesonal类的带参构造方法,再调用Student类的无参构造方法。

(6)上转型对象

上转型其实就是子类转成父类,我们先看下代码:

Student student;
student.setName("zhangsan");
student.setAge(16);
student.setScore(90);

Pesonal pesonal = student; // 上转型,子类转成父类

cout << "姓名:" << pesonal.getName() << endl;
cout << "年龄:" << pesonal.getAge() << endl;

Student有一个属性:成绩,由于Student继承Pesonal,相当于Student有三个属性:姓名、年龄、成绩;
Pesonal只有两个属性:姓名、年龄;
当Student赋值给Pesonal时,即:

Pesonal pesonal = student; // 上转型,子类转成父类

pesonal只能访问Pesonal类中的成员,不能访问Student中的成员。

如果需要父类转成子类,这样的转换属于降级转换,降级转换必须使用强制转换,父类转子类的强制转换和上转型相反。

(7)多重继承

C++可以多重继承,多重继承的一般形式为:

class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,…
{
    <派生类类体>
};

多个基类用逗号隔开。

子类可以调用多个父类的公有型和保护型的成员。

[本章完...]

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 223,207评论 6 521
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 95,455评论 3 400
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 170,031评论 0 366
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 60,334评论 1 300
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 69,322评论 6 398
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,895评论 1 314
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,300评论 3 424
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,264评论 0 277
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,784评论 1 321
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,870评论 3 343
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,989评论 1 354
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,649评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,331评论 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,814评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,940评论 1 275
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,452评论 3 379
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,995评论 2 361