C++虚函数详解
虚函数的含义
虚函数是C++中实现动态多态的核心机制,具有以下特性:
- 动态绑定:在运行时根据对象的实际类型决定调用哪个函数
- 基类声明:在基类中用
virtual关键字声明
- 派生类重写:可以在派生类中用相同签名重写
- 虚表机制:每个含虚函数的类都有一个虚函数表(vtable)
虚函数的使用方法
基本语法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Base { public: virtual void display() { cout << "Base class display" << endl; } };
class Derived : public Base { public: void display() override { cout << "Derived class display" << endl; } };
class Derived1 : public Base { public: void display() override { cout << "Derived class display" << endl; Base::display(); } };
|
使用场景
- 当需要通过基类指针/引用调用不同派生类的方法时
- 实现运行时多态,提升代码扩展性
关键点
1 2 3 4 5 6
| Base* ptr = new Derived(); ptr->display();
Base* ptr1 = new Derived1(); ptr1->display();
|
纯虚函数
1 2 3 4 5
| class Abstract { public: virtual void pureVirtual() = 0; };
|
注意事项
- 虚函数调用比普通函数调用有轻微性能开销
- 虚函数不能是静态成员函数
- 构造函数和析构函数不能是虚函数
- 虚函数表会增加对象的内存开销
虚表机制
虚表(Virtual Table,简称Vtable)是C++中实现运行时多态(动态多态)的核心机制,通过虚函数表存储虚函数地址,支持基类指针/引用调用派生类函数的功能
核心特性
基本构成
- 每个含虚函数的类都有唯一虚表,存储该类所有虚函数的函数指针
- 对象包含虚表指针(vptr),指向所属类的虚表
工作原理
- 调用虚函数时,通过对象vptr定位虚表,再根据偏移量调用对应函数
- 实现动态绑定(运行时确定调用目标)
继承关系
- 派生类继承基类虚表,覆写虚函数会修改对应指针
- 多继承时,对象可能包含多个vptr,每个指向对应基类虚表
内存布局
- 对象首地址存储vptr,随后是成员数据
- 虚表在编译时生成,vptr在运行时绑定
性能影响
- 虚函数调用比普通函数增加间接寻址开销
- 实际优化中可通过虚表指针替换等技术提升性能
此机制是C++多态实现的基础,理解虚表对掌握面向对象编程至关重要
相关问题
当基类和派生类都相同有虚函数时,调用哪个函数取决于对象的实际类型和调用方式。
调用规则
对象类型决定调用:
- 通过基类指针/引用调用时,实际调用派生类重写的虚函数
- 通过对象本身调用时,直接调用对象所属类的函数
动态绑定条件:
- 函数必须是虚函数(virtual)
- 必须通过指针或引用调用
- 不能是const成员函数(除非显式声明为const)
调用示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class Base { public: virtual void func() { cout << "Base::func()" << endl; } };
class Derived : public Base { public: void func() override { cout << "Derived::func()" << endl; } };
int main() { Base* ptr1 = new Derived; Base* ptr2 = new Base; Base obj1; Derived obj2; ptr1->func(); ptr2->func(); delete ptr1; delete ptr2; return 0; }
|
特殊场景处理
const对象调用:
1 2
| const Base* ptr = new Derived; ptr->func();
|
多重继承情况:
override与final:
- 使用override确保正确重写
- 使用final阻止进一步重写
底层实现原理
**虚函数表(vtable)**:
- 每个对象包含指向虚函数表的指针
- 编译器为每个类生成虚函数表
运行时多态:
- 调用时通过对象指针查找虚函数表
- 实际调用地址在运行时确定
函数是纯虚函数
纯虚函数的定义特征
语法标识:
- 在函数声明后使用
= 0表示纯虚函数
- 示例:
virtual void draw() = 0;
实现要求:
- 必须有声明但无函数体实现
- 强制要求派生类必须重写该函数
纯虚函数与虚函数的对比
| 特征 |
纯虚函数 |
虚函数 |
| 语法 |
= 0后缀 |
仅virtual关键字 |
| 实现 |
无函数体 |
有默认实现 |
| 类实例化 |
必须实现后才能实例化 |
可直接实例化 |
| 用途 |
定义接口规范 |
提供多态基础 |
判断方法
代码审查:
- 检查函数声明是否包含
= 0
- 确认函数是否有具体实现
类实例化测试:
- 尝试实例化基类,若失败则可能包含纯虚函数
- 查看是否强制要求派生类实现特定方法
编译器提示:
- 未实现纯虚函数时会报错
- 错误信息通常包含”pure virtual”字样