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();
}