C++ OOP 面试高频知识点 - 08
多态:静态多态(重载)、动态多态(虚函数)
1. 多态的基本概念
多态(Polymorphism) 是面向对象编程的核心原则之一,它允许我们使用统一的接口处理不同类型的对象。C++ 提供了两种多态形式:静态多态(编译时多态)和 动态多态(运行时多态)。
class Shape {
public:
virtual double area() const = 0;
};
class Rectangle : public Shape {
public:
Rectangle(double w, double h) : width(w), height(h) {}
double area() const override {
return width * height;
}
private:
double width, height;
};
class Circle : public Shape {
public:
Circle(double r) : radius(r) {}
double area() const override {
return 3.14159 * radius * radius;
}
private:
double radius;
};
void printArea(const Shape& shape) {
cout << "Area: " << shape.area() << endl;
}
int main() {
Rectangle rect(5, 10);
Circle circle(5);
printArea(rect); // 调用 Rectangle::area()
printArea(circle); // 调用 Circle::area()
return 0;
}
2. 静态多态(编译时多态)
静态多态是在编译阶段确定调用的函数,主要通过以下方式实现:
2.1 函数重载(Function Overload)
函数重载是指在同一个作用域内,函数名称相同但参数列表不同。
void print(int x) {
cout << "Integer: " << x << endl;
}
void print(double x) {
cout << "Double: " << x << endl;
}
void print(const string& s) {
cout << "String: " << s << endl;
}
int main() {
print(10); // 调用 print(int)
print(3.14); // 调用 print(double)
print("Hello"); // 调用 print(const string&)
return 0;
}
2.2 模板(Templates)
模板是 C++ 的泛型编程工具,可以根据类型自动生成函数或类。
template <typename T>
T add(T a, T b) {
return a + b;
}
int main() {
int x = add(1, 2); // 自动生成 int 版本
double y = add(1.5, 2.5); // 自动生成 double 版本
cout << "x = " << x << endl;
cout << "y = " << y << endl;
return 0;
}
3. 动态多态(运行时多态)
动态多态是在运行阶段确定调用的函数,主要通过虚函数实现。
3.1 虚函数(Virtual Functions)
虚函数是在基类中声明为 virtual 的成员函数。当在子类中重新实现虚函数时,会发生多态行为。
class Animal {
public:
virtual void makeSound() {
cout << "Animal makes a sound" << endl;
}
virtual ~Animal() {} // 虚析构函数
};
class Dog : public Animal {
public:
void makeSound() override {
cout << "Dog barks" << endl;
}
};
class Cat : public Animal {
public:
void makeSound() override {
cout << "Cat meows" << endl;
}
};
int main() {
Animal* animals[] = {new Dog(), new Cat()};
for (int i = 0; i < 2; ++i) {
animals[i]->makeSound(); // 运行时确定调用的函数
}
for (int i = 0; i < 2; ++i) {
delete animals[i];
}
return 0;
}
3.2 虚函数的实现机制
C++ 通过以下机制实现虚函数:
- 虚函数表(Virtual Table):每个包含虚函数的类都有一个虚函数表,存储该类的所有虚函数地址
- 虚指针(Virtual Pointer):每个对象都有一个虚指针,指向该类的虚函数表
- 动态绑定:当调用虚函数时,通过虚指针找到虚函数表,然后根据偏移量调用相应的函数
4. 静态多态与动态多态的比较
| 特性 | 静态多态 | 动态多态 |
|---|---|---|
| 确定时间 | 编译时 | 运行时 |
| 实现方式 | 函数重载、模板 | 虚函数 |
| 性能 | 较高(编译时绑定) | 较低(运行时绑定) |
| 灵活性 | 较低(需要明确类型) | 较高(支持继承和多态) |
| 代码复用 | 模板提供泛型编程 | 继承提供代码复用 |
5. 抽象类和接口
抽象类是包含至少一个纯虚函数的类。抽象类不能直接实例化,但可以作为基类。
class Shape {
public:
virtual double area() const = 0; // 纯虚函数
};
class Rectangle : public Shape {
public:
double area() const override {
return width * height;
}
private:
double width, height;
};
// Shape s; // 错误:抽象类不能实例化
6. 常见问题和回答
问题 1:静态多态和动态多态的区别?
- 静态多态:编译时确定调用的函数,通过重载和模板实现
- 动态多态:运行时确定调用的函数,通过虚函数实现
问题 2:为什么需要虚析构函数?
如果类中有虚函数,析构函数应该是虚函数,否则在通过基类指针删除子类对象时会导致内存泄漏。
class Base {
public:
virtual void foo() = 0;
~Base() { cout << "Base destructor" << endl; }
};
class Derived : public Base {
public:
Derived() { data = new int[100]; }
~Derived() {
delete[] data;
cout << "Derived destructor" << endl;
}
void foo() override {}
private:
int* data;
};
int main() {
Base* ptr = new Derived();
delete ptr; // 内存泄漏!
return 0;
}
问题 3:虚函数可以是模板函数吗?
不可以,虚函数不能是模板函数,因为模板参数在编译时确定,而虚函数在运行时确定。
class Base {
public:
template <typename T>
virtual void foo(T t) { // 错误:虚函数不能是模板函数
cout << t << endl;
}
};
7. 最佳实践
- 明确意图:根据需求选择合适的多态形式
- 设计清晰的接口:基类应该提供清晰的接口
- 使用虚析构函数:包含虚函数的类应该有虚析构函数
- 避免过度继承:继承层次应保持简单
- 使用抽象类:通过抽象类定义接口
总结
多态是 C++ 面向对象编程的核心概念之一,分为静态多态和动态多态两种形式。静态多态在编译时确定函数调用,提供较高的性能;动态多态在运行时确定函数调用,提供较高的灵活性。虚函数是实现动态多态的主要方式,而模板和函数重载是实现静态多态的常用方法。
练习建议: 1. 设计一个图形系统,支持多种形状的面积计算 2. 实现动态多态,通过基类指针调用不同子类的方法 3. 测试虚析构函数的重要性 4. 比较静态多态和动态多态的性能