目录
多态
1. 多态的概念
多态是面向对象编程中的一个重要概念,它分为两种主要类型:静态多态性(编译时多态性)和动态多态性(运行时多态性)。
2. 静态多态性(编译时多态性)
2.1 函数重载
静态多态性的一种体现是函数重载。 函数重载允许在同一个作用域中声明多个同名函数,它们的参数类型或个数不同。在编译时,根据函数名和参数信息,编译器会选择正确的函数实现。
示例:
// 静态多态性的例子:函数重载
void print(int value) {
// ...
}
void print(double value) {
// ...
}
2.2 运算符重载
运算符重载也是静态多态性的一种表现。通过为类定义特定的运算符重载函数,可以在编译时实现对用户自定义类型的运算符操作。
示例:
#include <iostream>
class Complex {
private:
double real;
double imag;
public:
Complex(double r, double i) : real(r), imag(i) {}
// 重载加法运算符
Complex operator+(const Complex& other) const {
return Complex(real + other.real, imag + other.imag);
}
// 打印复数
void display() const {
std::cout << real << " + " << imag << "i" << std::endl;
}
};
int main() {
Complex a(1.0, 2.0);
Complex b(3.0, 4.0);
// 使用重载的加法运算符
Complex result = a + b;
// 打印结果
result.display();
return 0;
}
3. 动态多态性(运行时多态性)
动态多态性通过虚函数和继承来实现。在运行时,程序能够根据对象的实际类型来调用相应的函数,这为构建灵活的程序提供了基础。
3.1 虚函数与继承
示例:
class Shape {
public:
//虚函数 virtual关键字
virtual void draw() const {
std::cout << "Drawing a shape." << std::endl;
}
};
class Rectangle : public Shape {
public:
// override 用于显式地标记在派生类中重写(覆盖)基类的虚函数。
// 在使用override关键字时,编译器会检查是否存在基类中的对应虚函数,如果不存在,则会产生编译错误,这样可以帮助开发者避免潜在的错误。
void draw() const override {
std::cout << "Drawing a rectangle." << std::endl;
}
void specialFunction() const {
std::cout << "Executing special function for rectangle." << std::endl;
}
};
// 接收基类对象的函数
void drawShape(const Shape& shape) {
shape.draw();
}
// 返回基类对象的函数
Shape getShape(bool isRectangle) {
if (isRectangle) {
return Rectangle();
}
else {
return Shape();
}
}
void Test08() {
// 使用派生类对象调用接收基类对象的函数
Rectangle rectangle;
drawShape(rectangle);
// 使用派生类对象作为返回值
Shape shape = getShape(true);
shape.draw();
}
输出结果:
Drawing a rectangle.
Drawing a shape.
第一行结果分析:
派生类Rectangle的对象可以传递给接收基类对象的函数drawShape,并且在该函数中,调用了派生类的重写函数draw。即父类指针或引用指向子类对象时,会调用子类重载父类的虚函数。从而出现运行时的多态性。
具体实现:const Shape& shape = rectangle; shape.draw();
第二行结果分析:
C++中,当派生类重写(override)了基类的虚函数(virtual function),通过基类指针或引用调用该虚函数时,会根据指针或引用指向的实际对象类型来决定调用哪个函数。
getShape(true)函数会触发以下操作:
(1)创建一个Rectangle类型的临时对象;
(2)由于返回类型是Shape,会执行派生类向基类的隐式类型转换。
所以该函数会返回一个Shape对象,而不是Rectangle对象。虽然Rectangle类重写了Shape类的draw函数,但是由于对象切片的问题,返回的对象类型被限定为Shape,因此调用shape.draw()时会执行Shape类的draw函数。
小结:
在上述例子中,派生类Rectangle的对象可以传递给接收基类对象的函数drawShape,并且在该函数中,调用了派生类的重写(override)函数draw。同时,我们通过getShape函数返回了一个基类对象,但是根据传入的参数,返回一个派生类对象Rectangle。 这样,我们可以在使用基类对象的地方,实际上使用了派生类对象的功能,而不需要直接关心派生类的具体实现。
这体现了面向对象编程中的多态性和封装性。
3.2 虚函数表(vtable)
动态多态性的实现依赖于虚函数表(vtable)的机制。每个对象都有一个指向虚函数表的指针,表中存储了类的虚函数地址。在运行时,通过这个指针来查找并调用相应的虚函数。
根据上面的例子,多态实现从底层来看:
Shape 对象内存储着虚函数指针,指针指向它的虚函数表,表中存储着虚函数draw()的地址。而子类Rectangle 继承Shape时,也会继承这个父类(Shape)的虚函数表。当子类重写父类的虚函数时,会修改继承父类的虚函数表,修改为子类实现的虚函数。最后当父类指针或者引用指向子类的对象时,调用的虚函数就是子类实现的虚函数,从而实现运行时的多态性。
4. 多态的优点
-
灵活性和可扩展性: 多态使得程序能够适应不同类型的对象,增加了代码的灵活性和可扩展性。
-
简化代码: 多态能够通过相同的接口处理不同类型的对象,简化了代码结构,提高了可读性。
-
降低耦合度: 通过多态,代码之间的耦合度降低了,子类可以独立地进行修改和扩展,不会影响其他部分的代码。
5. 总结:
总体而言,多态是面向对象编程的核心思想之一,它使得程序更易于理解、维护和扩展。通过不同形式的多态性,程序员可以更灵活地处理各种复杂的情况。
希望各位多多支持,码字不易,thank you!
欢迎关注🔎点赞👍收藏⭐️留言📝