C++中的繼承、多態(tài)和模板函數(shù)
一、繼承
1、屬性和方法的繼承
繼承可以更好的實(shí)現(xiàn)代碼的重用性
#include <stdlib.h>
#include <iostream>
using namespace std;
//開發(fā)者
class Developer {
protected:
char* language;
char* ide;
int age;
public:
void say() {
cout << "我是開發(fā)者" << endl;
}
};
//Android開發(fā)者
class AndroidDeveloper : public Developer {
public:
AndroidDeveloper() {
this->language = (char*)"Android+Kotlin";
this->ide = (char*)"Android Stuio";
}
//開發(fā)Android Application
void createAndroidApp() {
cout << "我使用" << this->ide << "開發(fā)了一款A(yù)ndroid應(yīng)用,使用了" << this->language << "語言" << endl;
}
private:
//Android 版本
char* androidVersion;
};
//所有開發(fā)者都有開發(fā)工作
void work(Developer& d) {
d.say();
}
void main() {
AndroidDeveloper androidDev;
androidDev.say();
androidDev.createAndroidApp();
//子類對(duì)象初始化父類類型的對(duì)象
Developer d1 = androidDev;
work(d1);
//父類類型的指針
Developer* d_p = &androidDev;
d_p->say();
//父類類型的引用
Developer d2 = androidDev;
d2.say();
getchar();
}
2、通過子類給父類構(gòu)造方法傳參
父類的構(gòu)造函數(shù)先調(diào)用;子類的析構(gòu)函數(shù)先調(diào)用
//開發(fā)者
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 構(gòu)造函數(shù)" << endl;
}
~Developer() {
cout << "Developer 析構(gòu)函數(shù)" << endl;
}
void say() {
cout << "我是開發(fā)者" << endl;
}
};
//Android開發(fā)者
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 構(gòu)造函數(shù)" << endl;
}
~AndroidDeveloper(){
cout << "AndroidDeveloper 析構(gòu)函數(shù)" << endl;
}
//開發(fā)Android Application
void createAndroidApp() {
cout << "我使用" << this->ide << "開發(fā)了一款A(yù)ndroid應(yīng)用,使用了" << this->language << "語言" << endl;
}
private:
//Android 版本
char* androidVersion;
};
void work(Developer& d) {
d.say();
}
//父類的構(gòu)造函數(shù)先調(diào)用
//子類的析構(gòu)函數(shù)先調(diào)用
void func() {
AndroidDeveloper androidDev((char*)"Kotlin", (char*)"Android Studio", 5, (char*)"5.0.1");
androidDev.say();
androidDev.createAndroidApp();
}
void main() {
func();
getchar();
}
輸出:
Developer 構(gòu)造函數(shù)
AndroidDeveloper 構(gòu)造函數(shù)
我是開發(fā)者
我使用Android Studio開發(fā)了一款A(yù)ndroid應(yīng)用,使用了Kotlin語言
AndroidDeveloper 析構(gòu)函數(shù)
Developer 析構(gòu)函數(shù)
3、繼承中父類和子類的權(quán)限繼承關(guān)系
| 基類中 | 繼承方式 | 子類中 |
|---|---|---|
| public | & public繼承 | => public |
| public | & protected繼承 | => protected |
| public | & private繼承 | => private |
| protected | & public繼承 | => protected |
| protected | & protected繼承 | => protected |
| protected | & private繼承 | => private |
| private | & public繼承 | => 子類無權(quán)訪問 |
| private | & protected繼承 | => 子類無權(quán)訪問 |
| private | & private繼承 | => 子類無權(quán)訪問 |
4、繼承的二義性
4.1 繼承的二義性定義
在某個(gè)類B同時(shí)繼承另一個(gè)類A的兩個(gè)或多個(gè)子類時(shí)(A1和A2),通過類B訪問類A的成員時(shí),會(huì)出現(xiàn)成員不明確的情況,即繼承的二義性
class A {
public:
char* name;
};
class A1 : public A {
};
class A2 : public A {
};
class B : public A1, public A2 {
};
void main() {
B b;
//報(bào)錯(cuò),提示B::name不明確
//b.name = (char*)"Jack";
//指定父類顯式調(diào)用
b.A1::name = (char*)"Rose";
getchar();
}
4.2 繼承的二義性定義解決方案
再遇到繼承的二義性時(shí),可使用虛繼承來解決繼承的二義性問題
虛繼承:不同路徑繼承來的同名成員只有一份拷貝
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ào)錯(cuò),提示B::name不明確
//b.name = (char*)"Jack";
//指定父類顯式調(diào)用
b.A1::name = (char*)"Rose";
getchar();
}
二、多態(tài)
- 多態(tài)是為了提高程序的擴(kuò)展性
- 動(dòng)態(tài)多態(tài):子類重寫父類的函數(shù),程序運(yùn)行過程中,決定哪一個(gè)函數(shù)被調(diào)用
- 靜態(tài)多態(tài):就是函數(shù)重載
1、虛函數(shù)
virtual 關(guān)鍵字修飾的函數(shù)叫虛函數(shù),用來實(shí)現(xiàn)多態(tài)
例如:
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 << "飛機(jī)起飛" << endl;
}
void Plane::land() {
cout << "飛機(jī)降落" << 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 << "直升飛機(jī)在原地起飛" << endl;
}
void Helicopter::land() {
cout << "直升飛機(jī)降落在屋頂" << endl;
}
Test.cpp
#include <stdlib.h>
#include <iostream>
#include "Plane.h"
#include "Helicopter.h"
using namespace std;
//業(yè)務(wù)函數(shù)
void runPlane(Plane &p) {
p.fly();
p.land();
}
void main() {
Plane p;
runPlane(p);
Helicopter h;
//在 Plane.h 和 Helicopter.h 中的函數(shù)上不使用 virtual 修飾時(shí),打印“飛機(jī)起飛”和“飛機(jī)降落”
//使用 virtual 修飾時(shí),打印“直升飛機(jī)在原地起飛”和“直升飛機(jī)降落在屋頂”,實(shí)現(xiàn)多態(tài)
runPlane(h);
getchar();
}
2、發(fā)生動(dòng)態(tài)多態(tài)的條件
- 使用繼承
- 父類的引用或指針指向子類的對(duì)象
- 函數(shù)的重寫
3、純虛函數(shù)(抽象類)
- 當(dāng)一個(gè)類具有一個(gè)純虛函數(shù)時(shí),這個(gè)類就是抽象類
- 抽象類不能被實(shí)例化
- 子類繼承抽象類,必須要實(shí)現(xiàn)純虛函數(shù),如果沒有重新,子類也是抽象類
//形狀
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、接口
接口只是邏輯上的劃分,語法上跟抽象類的寫法沒有區(qū)別
//可以看作一個(gè)接口
class Drawable{
virtual void draw() = 0;
}
5、抽象類的作用
為了繼承約束,子類必須按照約束實(shí)現(xiàn)
//可以看作一個(gè)接口
class Drawable{
virtual void draw() = 0;
}
二、模板函數(shù)(泛型)
函數(shù)模板類似于泛型,用于在業(yè)務(wù)相同,參數(shù)類型不同時(shí)進(jìn)行聲明,在使用過程中,根據(jù)實(shí)際類型進(jìn)行推導(dǎo)
template <typename T,typename Z>
//交換兩個(gè)變量的值
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();
}