1. 概念
1.1 多态
不同类的对象对相同的方法或函数产生有不同的反应。多态的实现依赖于虚函数
静态多态和动态多态
静态多态(编译时多态)
- 这是通过方法重载实现的一种多态性形式。
- 在编译时,编译器确定使用哪个方法。重载方法的选择发生在编译阶段,因此它是静态的,编译器会确定调用哪个方法。
动态多态(运行时多态)
- 这是通过方法重写和继承实现的一种多态性形式。
- 在运行时,程序确定使用哪个方法。
1.2 虚函数
虚函数是指使用了修饰符virtua修饰过后的函数,而且定义虚函数的函数必须为类的成员函数,虚函数被继承后所继承的派生类都是为虚函数,析构函数可以定义为虚函数,但是构造函数(与友员函数)却不能定义为虚函数。
2. 虚函数
2.1 作用
虚函数的作用主要是实现了多态的机制。基类定义虚函数,子类可以重写该函数;在派生类中对积累定义的虚函数进行重写时,需要在派生类中声明该方法为虚方法。
2.2 析构函数可以为虚函数吗
当使用多态特性,让基类指针指向派生类对象时,如果析构函数不是虚函数,通过基类指针销毁派生类对象时,会调用静态绑定的析构函数,也就是基类的析构函数,从而只能销毁属于基类的元素,导致派生类析构不完全,程序就会出现资源泄露或未定义行为。
2.3 构造函数可以为虚函数吗
在C 中,构造函数(包括拷贝构造函数和移动构造函数)不能声明为虚函数。虚函数在运行时通过对象的虚函数表(vtable)来调用,而构造函数在对象被创建之前执行,因此在对象存在之前虚函数表也不存在,无法实现虚函数的多态性。
2.4 纯虚函数
纯虚函数必须在基类中定义,没有具体的实现代码,只有函数声明。它规定派生类必须提供该函数的具体实现。 注意:纯虚函数的类无法被实例化,只能作为基类来派生其他类。派生类必须提供对应的纯虚函数的具体实现。
示例(定义纯虚函数的语法是在函数声明后面加上 = 0
):
class AbstractBase {
public:
virtual void pureVirtualFunction1() = 0; // 第一个纯虚函数
virtual void pureVirtualFunction2(int x) = 0; // 第二个纯虚函数带参数
virtual void pureVirtualFunction3(double y, const std::string& str) = 0; // 第三个纯虚函数带多个参数
};
};
class Derived : public AbstractBase {
public:
void pureVirtualFunction1() override { //override表明重写虚函数,可不加
// 提供具体的实现
// ...
}
void pureVirtualFunction2(int x) override {
// 提供具体的实现
// ...
}
void pureVirtualFunction3(double y, const std::string& str) override {
// 提供具体的实现
// ...
}
};
2.5 虚表原理?虚表指针存放在哪里?
- 虚表的工作原理:
- 每个包含虚函数的C 类都有一个对应的虚函数表。
- 虚表中存储了该类中的虚函数地址。
- 每个对象都包含一个指向其类的虚表指针。
- 当你调用一个虚函数时,实际上是通过对象的虚表指针来查找适当的虚函数地址,然后调用该函数。
- 虚表指针存放在哪里:通常存放在对象内部,即对象的地址就是虚表指针的地址。
2.6 虚函数是类的定义出现还是对象的时候出现
虚函数的定义在类的定义中,而不是在创建对象时出现。
2.4 函数重载和重写区别
- 范围区别:重写和被重写的函数在不同的类中,重载和被重载的函数在同一类中。
- 参数区别:重写的函数名,参数个数,类型,顺序以及返回值类型完全一样,而重载的参数个数,类型,顺序至少有一个不同。
- virtual的区别:重写的基类函数必须要有virtual修饰,重载函数和被重载函数可以被virtual修饰,也可以没有
2.5 C 多态示例
代码语言:javascript复制#include <iostream>
using namespace std;
class Shape {
public:
virtual double area() const = 0; // 纯虚函数
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
double area() const {
return 3.14159265359 * radius * radius;
}
};
class Rectangle : public Shape {
private:
double width;
double height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
double area() const {
return width * height;
}
};
int main() {
Circle circle(5.0);
Rectangle rectangle(4.0, 6.0);
Shape* shape1 = &circle;
Shape* shape2 = &rectangle;
std::cout << "面积1: " << shape1->area() << std::endl;
std::cout << "面积2: " << shape2->area() << std::endl;
return 0;
}
致读者
非知之难,行之为难;非行之难,终之斯难