(四)C++中的继承、多态和模板函数

C++中的继承、多态和模板函数

一、继承

1、属性和方法的继承

继承可以更好的实现代码的重用性

#include <stdlib.h>
#include <iostream>

using namespace std;

//开发者
class Developer {
protected:
    char* language;
    char* ide;
    int age;
public:
    void say() {
        cout << "我是开发者" << endl;
    }
};

//Android开发者
class AndroidDeveloper : public Developer {
public:
    AndroidDeveloper() {
        this->language = (char*)"Android+Kotlin";
        this->ide = (char*)"Android Stuio";
    }
    //开发Android Application
    void createAndroidApp() {
        cout << "我使用" << this->ide << "开发了一款Android应用,使用了" << this->language << "语言" << endl;
    }
private:
    //Android 版本
    char* androidVersion;

};

//所有开发者都有开发工作
void work(Developer& d) {
    d.say();
}

void main() {
    AndroidDeveloper androidDev;
    androidDev.say();
    androidDev.createAndroidApp();
    //子类对象初始化父类类型的对象
    Developer d1 = androidDev;
    work(d1);
    //父类类型的指针
    Developer* d_p = &androidDev;
    d_p->say();
    //父类类型的引用
    Developer d2 = androidDev;
    d2.say();

    getchar();
}

2、通过子类给父类构造方法传参

父类的构造函数先调用;子类的析构函数先调用

//开发者
class Developer {
protected:
    char* language;
    char* ide;
    int age;
public:
    Developer(char * language, char* ide, int age) {
        this->language = language;
        this->ide = ide;
        this->age = age;
        cout << "Developer 构造函数" << endl;
    }
    ~Developer() {
        cout << "Developer 析构函数" << endl;
    }
    void say() {
        cout << "我是开发者" << endl;
    }
};

//Android开发者
class AndroidDeveloper : public Developer {
public:
    AndroidDeveloper(char* language, char* ide, int age, char* androidVersion) : Developer(language, ide, age) {
        this->language = language;
        this->ide = ide;
        this->age = age;
        cout << "AndroidDeveloper 构造函数" << endl;
    }
    ~AndroidDeveloper(){
        cout << "AndroidDeveloper 析构函数" << endl;
    }
    //开发Android Application
    void createAndroidApp() {
        cout << "我使用" << this->ide << "开发了一款Android应用,使用了" << this->language << "语言" << endl;
    }
private:
    //Android 版本
    char* androidVersion;

};

void work(Developer& d) {
    d.say();
}

//父类的构造函数先调用
//子类的析构函数先调用
void func() {
    AndroidDeveloper androidDev((char*)"Kotlin", (char*)"Android Studio", 5, (char*)"5.0.1");
    androidDev.say();
    androidDev.createAndroidApp();
}

void main() {
    func();
    getchar();
}

输出:

Developer 构造函数
AndroidDeveloper 构造函数
我是开发者
我使用Android Studio开发了一款Android应用,使用了Kotlin语言
AndroidDeveloper 析构函数
Developer 析构函数

3、继承中父类和子类的权限继承关系

基类中 继承方式 子类中
public & public继承 => public
public & protected继承 => protected
public & private继承 => private
protected & public继承 => protected
protected & protected继承 => protected
protected & private继承 => private
private & public继承 => 子类无权访问
private & protected继承 => 子类无权访问
private & private继承 => 子类无权访问

4、继承的二义性

4.1 继承的二义性定义

在某个类B同时继承另一个类A的两个或多个子类时(A1和A2),通过类B访问类A的成员时,会出现成员不明确的情况,即继承的二义性

class A {
public:
    char* name;
};

class A1 : public A {

};

class A2 : public A {

};

class B : public A1, public A2 {

};

void main() {
    B b;
    //报错,提示B::name不明确
    //b.name = (char*)"Jack";

    //指定父类显式调用
    b.A1::name = (char*)"Rose";
    getchar();
}

4.2 继承的二义性定义解决方案

再遇到继承的二义性时,可使用虚继承来解决继承的二义性问题
虚继承:不同路径继承来的同名成员只有一份拷贝

class A {
public:
    char* name;
};

class A1 : virtual public A {

};

class A2 : virtual public A {

};

class B : public A1, public A2 {

};

void main() {
    B b;
    //报错,提示B::name不明确
    //b.name = (char*)"Jack";

    //指定父类显式调用
    b.A1::name = (char*)"Rose";
    getchar();
}

二、多态

  • 多态是为了提高程序的扩展性
  • 动态多态:子类重写父类的函数,程序运行过程中,决定哪一个函数被调用
  • 静态多态:就是函数重载

1、虚函数

virtual 关键字修饰的函数叫虚函数,用来实现多态

例如:

Plane.h

#pragma once

class Plane {
public:
    virtual void fly();
    virtual void land();
};

Plane.cpp

#include "Plane.h"
#include <stdlib.h>
#include <iostream>

using namespace std;
void Plane::fly() {
    cout << "飞机起飞" << endl;
}

void Plane::land() {
    cout << "飞机降落" << endl;
}

Helicopter.h

#pragma once
#include "Plane.h"

class Helicopter : public Plane {
public:
    virtual void fly();
    virtual void land();
};

Helicopter.cpp

#include "Helicopter.h"
#include <stdlib.h>
#include <iostream>

using namespace std;
void Helicopter::fly() {
    cout << "直升飞机在原地起飞" << endl;
}

void Helicopter::land() {
    cout << "直升飞机降落在屋顶" << endl;
}

Test.cpp

#include <stdlib.h>
#include <iostream>
#include "Plane.h"
#include "Helicopter.h"
using namespace std;

//业务函数

void runPlane(Plane &p) {
    p.fly();
    p.land();
}

void main() {
    Plane p;
    
    runPlane(p);
    Helicopter h;
    //在 Plane.h 和 Helicopter.h 中的函数上不使用 virtual 修饰时,打印“飞机起飞”和“飞机降落”
    //使用 virtual 修饰时,打印“直升飞机在原地起飞”和“直升飞机降落在屋顶”,实现多态
    runPlane(h);
    getchar();
}

2、发生动态多态的条件

  • 使用继承
  • 父类的引用或指针指向子类的对象
  • 函数的重写

3、纯虚函数(抽象类)

  • 当一个类具有一个纯虚函数时,这个类就是抽象类
  • 抽象类不能被实例化
  • 子类继承抽象类,必须要实现纯虚函数,如果没有重新,子类也是抽象类
//形状
class Shape {
public:
    virtual void sayArea() = 0;
};

//圆
class Circle : public Shape {
private:
    int r;
public:
    Circle(int r) {
        this->r = r;
    }
    void sayArea() {
        cout << "圆的面积:" << 3.14 * r * r << endl;
    }
};

void main() {
    Circle c(5);
    c.sayArea();
    getchar();
}

4、接口

接口只是逻辑上的划分,语法上跟抽象类的写法没有区别

//可以看作一个接口
class Drawable{
    virtual void draw() = 0;
}

5、抽象类的作用

为了继承约束,子类必须按照约束实现

//可以看作一个接口
class Drawable{
    virtual void draw() = 0;
}

二、模板函数(泛型)

函数模板类似于泛型,用于在业务相同,参数类型不同时进行声明,在使用过程中,根据实际类型进行推导

template <typename T,typename Z>
//交换两个变量的值
void swap(T& a, Z& b){
    T tmp = 0;
    tmp = a;
    a = b;
    b = tmp;
}

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

推荐阅读更多精彩内容