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