(四)C++中的繼承、多態和模板函數 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();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章