C++虚函数

C++虚函数详解

虚函数的含义

虚函数是C++中实现‌动态多态‌的核心机制,具有以下特性:

  1. 动态绑定‌:在运行时根据对象的实际类型决定调用哪个函数
  2. 基类声明‌:在基类中用virtual关键字声明
  3. 派生类重写‌:可以在派生类中用相同签名重写
  4. 虚表机制‌:每个含虚函数的类都有一个虚函数表(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(); // 输出: Derived class display (动态绑定)

Base* ptr1 = new Derived1();
ptr1->display(); // 输出: Derived class display (动态绑定)
// Base class display

纯虚函数

1
2
3
4
5
class Abstract {
public:
virtual void pureVirtual() = 0; // 纯虚函数
};
// 不能实例化,必须被派生类实现

注意事项

  1. 虚函数调用比普通函数调用有轻微性能开销
  2. 虚函数不能是静态成员函数
  3. 构造函数和析构函数不能是虚函数
  4. 虚函数表会增加对象的内存开销

虚表机制

虚表(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; // 调用Base::func()
Derived obj2; // 调用Derived::func()

ptr1->func(); // 调用Derived::func()(动态绑定)
ptr2->func(); // 调用Base::func()(动态绑定)

delete ptr1;
delete ptr2;
return 0;
}

特殊场景处理

const对象调用‌:

1
2
const Base* ptr = new Derived;
ptr->func(); // 调用Derived::func()

多重继承情况‌:

  • 通过最底层的类指针调用时,会调用最底层类的实现

override与final‌:

  • 使用override确保正确重写
  • 使用final阻止进一步重写

底层实现原理

‌**虚函数表(vtable)**‌:

  • 每个对象包含指向虚函数表的指针
  • 编译器为每个类生成虚函数表

运行时多态‌:

  • 调用时通过对象指针查找虚函数表
  • 实际调用地址在运行时确定

函数是纯虚函数

纯虚函数的定义特征

语法标识‌:

  • 在函数声明后使用= 0表示纯虚函数
  • 示例:virtual void draw() = 0;

实现要求‌:

  • 必须有声明但无函数体实现
  • 强制要求派生类必须重写该函数

纯虚函数与虚函数的对比

特征 纯虚函数 虚函数
语法 = 0后缀 virtual关键字
实现 无函数体 有默认实现
类实例化 必须实现后才能实例化 可直接实例化
用途 定义接口规范 提供多态基础

判断方法

代码审查‌:

  • 检查函数声明是否包含= 0
  • 确认函数是否有具体实现

类实例化测试‌:

  • 尝试实例化基类,若失败则可能包含纯虚函数
  • 查看是否强制要求派生类实现特定方法

编译器提示‌:

  • 未实现纯虚函数时会报错
  • 错误信息通常包含”pure virtual”字样