深入理解C++多态:从概念到实现
目录
前言
一、多态的概念
二、多态的实现条件
三、虚函数与重写
3.1 虚函数
3.2 虚函数重写
四、抽象类
五、多态的原理
5.1 虚函数表(虚表)
5.2 多态调用过程
六、继承中的虚表
6.1 单继承
6.2 多继承
七、常见问题
八、面试题解析
总结
前言
多态是面向对象编程的三大特性之一(封装、继承、多态),也是C++中最为强大的特性之一。它允许我们以统一的方式处理不同类型的对象,极大地提高了代码的灵活性和可扩展性。本文将全面解析C++多态的概念、实现原理以及相关技术细节。
一、多态的概念
多态(Polymorphism)源自希腊语,意为"多种形态"。在编程中,它指的是同一个行为在不同对象上表现出不同的状态。
生活中的例子:
- 买票行为:普通人全价,学生半价,军人优先
- 支付宝红包:不同用户扫码获得不同金额的红包
这些例子都体现了"同一行为,不同表现"的多态思想。
二、多态的实现条件
在C++中,要实现多态需要满足两个基本条件:
1. 必须通过基类的指针或引用调用虚函数
2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};class Student : public Person {
public:virtual void BuyTicket() { cout << "买票-半价" << endl; }
};void Func(Person& p) {p.BuyTicket(); // 多态调用
}
三、虚函数与重写
3.1 虚函数
使用`virtual`关键字修饰的成员函数称为虚函数:
virtual 返回类型 函数名(参数列表);
3.2 虚函数重写
派生类中重写基类虚函数时需要满足"三同"原则:
- 函数名相同
- 参数列表相同
- 返回值类型相同(协变例外)
协变:派生类重写基类虚函数时,返回值类型可以是基类虚函数返回类型的派生类。
class A {};
class B : public A {};class Person {
public:virtual A* f() { return new A; }
};class Student : public Person {
public:virtual B* f() { return new B; } // 协变
};
四、抽象类
包含纯虚函数的类称为抽象类(接口类),不能实例化对象。
class Car {
public:virtual void Drive() = 0; // 纯虚函数
};
抽象类强制派生类实现特定接口,体现了接口继承的思想。
五、多态的原理
5.1 虚函数表(虚表)
每个包含虚函数的类都有一个虚表,其中存储了虚函数的地址。对象中包含一个指向虚表的指针(vfptr)。
class Base {
public:virtual void Func1() { /*...*/ }virtual void Func2() { /*...*/ }
private:int _b = 1;
};
`sizeof(Base)`在32位系统下为8字节(4字节vfptr + 4字节int)。
5.2 多态调用过程
1. 通过对象的vfptr找到虚表
2. 在虚表中查找对应的虚函数
3. 调用该函数
这种运行时确定调用函数的过程称为动态绑定
六、继承中的虚表
6.1 单继承
派生类虚表包含:
1. 基类虚函数(被重写的会被覆盖)
2. 派生类新增的虚函数
6.2 多继承
派生类会有多个虚表(对应每个基类),未重写的虚函数放在第一个基类的虚表中。
七、常见问题
1. inline函数可以是虚函数吗?
可以,但编译器会忽略inline属性。
2. 静态成员可以是虚函数吗?
不能,因为没有this指针。
3. 构造函数可以是虚函数吗?
不能,因为虚表指针在构造函数中初始化。
4. 析构函数应该是虚函数吗?
基类析构函数应该是虚函数,确保正确调用派生类析构函数。
八、面试题解析
8.1 选择题示例
题目:以下程序输出结果是什么?
class A {
public:virtual void func(int val = 1) { cout << "A->" << val << endl; }virtual void test() { func(); }
};class B : public A {
public:void func(int val = 0) { cout << "B->" << val << endl; }
};int main() {B* p = new B;p->test();return 0;
}
答案:B->1
解析:虽然B重写了func,但默认参数在编译时确定,使用的是基类的默认参数。
8.2 问答题示例
题目:多态的实现原理?
答案:通过虚函数表和虚表指针实现。每个包含虚函数的类有一个虚表,对象中包含指向虚表的指针。调用虚函数时,通过指针找到虚表,再找到对应的函数地址进行调用。
总结
多态是C++中强大而复杂的特性,理解其底层实现原理对于编写高效、灵活的面向对象程序至关重要。通过本文的讲解,希望读者能够深入理解多态的概念、实现方式以及相关技术细节,在实际开发中灵活运用这一强大特性。