提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 一、多态是什么?
- 多态是面向对象编程中的一个重要概念,指的是同一个函数在不同的对象上具有不同的行为。具体来说,多态是通过虚函数实现的。
- 二、多态的种类:静态多态和动态多态
- ①静态多态:如函数重载与运算符重载
- ②动态多态:派生类与虚函数
- 语法:基类对该多态函数用virtual声明。
- 三、函数重写与函数重载的区别
- 四、通过基类的指针或引用来访问派生类对象(重要)
- 五、多态与虚函数的内部实现原理
- 六、多态有什么用?应用场合?
- 七、纯虚函数和抽象类
- 1.在多态中,父类的虚函数实现是没有意义的,调用的主要是子类中重写的内容,所以父类虚函数可改写为纯虚函数。
- 2.只要一个类里有了纯虚函数,这个类就是抽象类了。抽象类无法被实例化对象。
- 3.子类必须重写抽象类的纯虚函数,否则也属于抽象类。
一、多态是什么?
多态是面向对象编程中的一个重要概念,指的是同一个函数在不同的对象上具有不同的行为。具体来说,多态是通过虚函数实现的。
二、多态的种类:静态多态和动态多态
①静态多态:如函数重载与运算符重载
静态多态是指在编译时就确定了函数的调用方式,也称为编译时多态。C++中的函数重载就是一种静态多态,编译器在编译时就根据函数参数的类型和数量来确定调用哪个函数。
②动态多态:派生类与虚函数
动态多态是指在运行时根据对象的类型来确定函数的调用方式,也称为运行时多态。C++中的虚函数就是一种动态多态,当通过基类指针或引用调用虚函数时,实际上会根据指针或引用所指向的对象类型来调用相应的函数实现。
语法:基类对该多态函数用virtual声明。
三、函数重写与函数重载的区别
函数重写(override)和函数重载(overload)是两种不同的概念。
函数重载是指在同一个作用域内,可以定义多个同名函数,但是它们的参数列表不同。编译器会根据调用时提供的参数类型和数量,自动匹配合适的函数进行调用。函数重载可以提高代码的可读性和可维护性,但是需要注意避免过度使用,以免造成混淆。
函数重写是指在派生类中重新定义基类中已有的虚函数,使得派生类对象在调用该函数时,会优先调用派生类中的函数,而不是基类中的函数。函数重写可以实现运行时多态,提高代码的灵活性和可扩展性。
class animal {
public:virtual void print() {cout << "hello world" << endl;}
};class cat :public animal {
public:void print() {//重写基类的成员函数cout << "hello world cat" << endl;}void print(int n) {//重载成员函数for (int i = 0; i < n; i++) {cout << "hello world cat" << endl;}}
};int main() {animal a;cat cat1;a.print(); // 输出 "hello world"cat1.print(); // 输出 "hello world cat"cat1.print(3); // 输出三次 "hello world cat"return 0;
}
四、通过基类的指针或引用来访问派生类对象(重要)
多态需要通过基类的指针或引用来访问派生类对象。通过将派生类对象赋值给基类指针或引用,可以实现对派生类对象的统一访问
什么意思呢?就是说基类会有很多个派生类,我不想通过一个个实例化派生类的对象来调用其各自的函数,而是想直接通过基类来统一的管理与调用。
class animal {
public:virtual void sound() {cout << "animal sound" << endl;}
};class cat :public animal {
public:void sound() {cout << "meow" << endl;}
};class dog :public animal {
public:void sound() {cout << "woof" << endl;}
};int main() {animal* animals[3];animals[0] = new animal();animals[1] = new cat();animals[2] = new dog();for (int i = 0; i < 3; i++) {animals[i]->sound();}return 0;
}
mian函数是核心,我直接建立了一个类animal类型的指针数组,指针0指向类animal,指针1和2分别指向类cat与dog。这样的话,才能保证不同指针调用的是不同类的多态函数。如果不加virtual修饰,则三个指针都会调用基类函数。
五、多态与虚函数的内部实现原理
多态的实现依赖于虚函数的内部实现原理。在面向对象编程中,虚函数是通过虚函数表(vtable)和虚函数指针(vptr)来实现多态的。
虚函数表是一个存储了虚函数地址的表格,每个包含虚函数的类都有一个对应的虚函数表。虚函数表中的每个条目都指向相应虚函数的地址。当一个类被定义为包含虚函数时,编译器会在该类的对象中插入一个隐藏的指针,称为虚函数指针(vptr)。虚函数指针指向该类的虚函数表。
当通过基类指针或引用调用虚函数时,编译器会使用虚函数指针来查找虚函数表,并根据对象的实际类型找到对应的虚函数地址进行调用。这样就实现了在运行时根据对象的实际类型来确定调用的函数,从而实现了多态。
六、多态有什么用?应用场合?
我举一个例子:
实际工作中,需要开发一个计算器的项目,如果我只定义一个计数器类,在该类里面写函数,可以使用多态来处理不同类型的数据对象。
开发中的原则上:要求修改是闭合的,拓展是开放的。
所以,对于一个基类,最好只是声明一个大纲,而正式的各种不同功能的实现,需要通过多态,在派生类中完成。
#include <iostream>// 基类 Calculator
class Calculator {
public:virtual double calculate(double num1, double num2) {return 0;
}
};// 加法计算器类
class AddCalculator : public Calculator {
public:double calculate(double num1, double num2) {return num1 + num2;}
};// 减法计算器类
class SubCalculator : public Calculator {
public:double calculate(double num1, double num2) {return num1 - num2;}
};int main() {// 创建加法计算器对象Calculator* addCalc = new AddCalculator();double result1 = addCalc->calculate(5.0, 3.0);std::cout << "加法计算结果: " << result1 << std::endl;// 创建减法计算器对象Calculator* subCalc = new SubCalculator();double result2 = subCalc->calculate(5.0, 3.0);std::cout << "减法计算结果: " << result2 << std::endl;delete addCalc;delete subCalc;return 0;
}
我如果后续想对加法、减法进行修改或者拓展,就可以完全不管其他乘或除部分的代码,只负责这一个派生类即可。可以通过多态的方式,统一地操作不同类型的计算器对象,可读性更强了。
七、纯虚函数和抽象类
1.在多态中,父类的虚函数实现是没有意义的,调用的主要是子类中重写的内容,所以父类虚函数可改写为纯虚函数。
纯虚函数是在基类中声明但没有实现的虚函数。它的声明形式为在函数声明后面加上= 0。纯虚函数没有函数体,因此派生类必须实现它们才能被实例化。
例:
// 抽象类 Shape
class Shape {
public:virtual double getArea() const = 0; // 纯虚函数virtual double getPerimeter() const = 0; // 纯虚函数
};
2.只要一个类里有了纯虚函数,这个类就是抽象类了。抽象类无法被实例化对象。
3.子类必须重写抽象类的纯虚函数,否则也属于抽象类。
// 派生类 Circle
class Circle : public Shape {
private:double radius;public:Circle(double r) : radius(r) {}double getArea() const override {return 3.14 * radius * radius;}double getPerimeter() const override {return 2 * 3.14 * radius;}
};// 派生类 Rectangle
class Rectangle : public Shape {
private:double length;double width;public:Rectangle(double l, double w) : length(l), width(w) {}double getArea() const override {return length * width;}double getPerimeter() const override {return 2 * (length + width);}
};
主程序:
int main() {Circle circle(5.0);Rectangle rectangle(4.0, 6.0);std::cout << "圆的面积: " << circle.getArea() << std::endl;std::cout << "圆的周长: " << circle.getPerimeter() << std::endl;std::cout << "矩形的面积: " << rectangle.getArea() << std::endl;std::cout << "矩形的周长: " << rectangle.getPerimeter() << std::endl;return 0;
}