目录
1. 虚函数基础概念
1.1 什么是虚函数
虚函数是C++实现运行时多态的核心机制,通过 virtual 关键字声明:
class Base {
public:
virtual void show() { // 虚函数声明
cout << "Base show()" << endl;
}
virtual ~Base() {} // 虚析构函数
};
1.2 虚函数的特点
- 动态绑定:运行时根据对象实际类型调用对应函数
- 继承性:派生类可以重写(override)基类虚函数
- 多态性:通过基类指针/引用调用派生类实现
2. 虚函数实现原理
2.1 虚函数表(vtable)
每个包含虚函数的类都有一个虚函数表:
- 编译器自动生成
- 存储该类所有虚函数的地址
- 对象内存布局中包含指向vtable的指针(vptr)
class Animal {
public:
virtual void eat() = 0;
virtual void sleep() { /*...*/ }
};
class Dog : public Animal {
public:
void eat() override { /*...*/ }
void sleep() override { /*...*/ }
};
内存布局示例:
2.2 虚函数调用过程
- 通过对象找到vptr
- 通过vptr找到vtable
- 从vtable中获取函数地址
- 调用函数
对应的汇编代码示例:
3. 虚函数关键特性
3.1 覆盖(override)与隐藏(hiding)
class Base {
public:
virtual void func1() {}
void func2() {}
};
class Derived : public Base {
public:
void func1() override {} // 正确覆盖
void func2() {} // 隐藏基类func2
};
3.2 final关键字
C++11引入,禁止派生类覆盖:
class Base {
public:
virtual void foo() final {}
};
class Derived : public Base {
void foo() {} // 编译错误
};
3.3 纯虚函数与抽象类
class Shape { // 抽象类
public:
virtual double area() = 0; // 纯虚函数
};
4. 虚函数性能分析
4.1 开销来源
- 空间开销:
- 每个对象增加vptr(通常4/8字节)
- 每个类需要vtable存储空间
- 时间开销:
- 间接调用增加1-2条指令
- 可能影响内联优化
4.2 性能优化建议
- 避免在性能关键路径过度使用虚函数
- 对不需要多态的类不要使用虚函数
- 考虑使用CRTP模式编译期多态
5. 高级虚函数技术
5.1 虚析构函数
class Base {
public:
virtual ~Base() {} // 必须为虚
};
class Derived : public Base {
~Derived() override {} // 自动成为虚函数
};
Base* p = new Derived();
delete p; // 正确调用Derived的析构函数
5.2 构造函数中的虚函数
class Base {
public:
Base() { callVirtual(); } // 危险!
virtual void callVirtual() { /*...*/ }
};
注意: 在构造函数中虚函数机制未完全建立,会调用当前类的实现
5.3 虚函数默认参数
class Base {
public:
virtual void foo(int x = 1) { /*...*/ }
};
class Derived : public Base {
public:
void foo(int x = 2) override { /*...*/ }
};
Base* p = new Derived();
p->foo(); // 使用Base的默认参数1,但调用Derived的实现
最佳实践:避免在虚函数中使用默认参数
6. 虚函数与对象模型
6.1 多重继承下的虚函数
class Base1 {
public:
virtual void foo() {}
};
class Base2 {
public:
virtual void bar() {}
};
class Derived : public Base1, public Base2 {
public:
void foo() override {}
void bar() override {}
};
内存布局:
6.2 虚继承中的虚函数
class Base {
public:
virtual void foo() {}
};
class Derived1 : virtual public Base {
void foo() override {}
};
class Derived2 : virtual public Base {
void foo() override {}
};
class Final : public Derived1, public Derived2 {
void foo() override {} // 必须重写解决歧义
};
7. 现代C++中的虚函数
7.1 override关键字
class Derived : public Base {
public:
void foo() override {} // 明确表示覆盖
};
优势:
- 编译器检查是否正确覆盖
- 提高代码可读性
7.2 使用final
class Base final { // 禁止继承
public:
virtual void foo() final {} // 禁止覆盖
};
7.3 协变返回类型
class Base {
public:
virtual Base* clone() const { /*...*/ }
};
class Derived : public Base {
public:
Derived* clone() const override { /*...*/ } // 协变返回
};
8. 虚函数设计模式
8.1 模板方法模式
class Algorithm {
public:
void run() { // 非虚
init(); // 虚
process(); // 虚
cleanup(); // 虚
}
protected:
virtual void init() = 0;
virtual void process() = 0;
virtual void cleanup() {}
};
8.2 工厂模式
class Product {
public:
virtual void use() = 0;
};
class Factory {
public:
virtual Product* create() = 0;
virtual ~Factory() {}
};
9. 调试与问题排查
9.1 查看虚函数表
使用gdb:
(gdb) info vtbl obj
9.2 常见问题
- 对象切片:
Derived d;
Base b = d; // 切片,虚函数表被截断
- 忘记虚析构函数:
Base* p = new Derived();
delete p; // 如果Base析构非虚,Derived部分泄漏
10. 性能优化实战
10.1 虚函数替代方案
10.2 虚函数缓存优化
class Widget {
public:
void perform() {
if (!cached) {
result = compute(); // 虚调用
cached = true;
}
return result;
}
private:
virtual int compute() = 0;
bool cached = false;
int result;
};
总结
虚函数是C++多态的核心机制,理解其底层实现有助于:
- 编写更高效的代码
- 避免常见陷阱
- 设计更灵活的架构
- 更好地进行调试和优化
最佳实践建议:
- 多态基类声明虚析构函数
- 使用override明确覆盖关系
- 避免在构造/析构中调用虚函数
- 对性能关键代码考虑替代方案