C++ 虚函数
外观
C++虚函数是面向对象编程中实现运行时多态的核心机制,允许通过基类指针或引用调用派生类的成员函数。本条目将详细讲解其工作原理、语法规则、应用场景及常见问题。
基本概念[编辑 | 编辑源代码]
虚函数(Virtual Function)是通过在基类中使用virtual
关键字声明的成员函数,其行为可以在派生类中被重写(override)。当通过基类指针或引用调用虚函数时,程序会根据对象的实际类型动态决定调用哪个版本的函数,这一过程称为动态绑定(Dynamic Binding)或晚期绑定(Late Binding)。
关键特性[编辑 | 编辑源代码]
- 动态绑定:运行时决定调用的函数版本。
- 重写规则:派生类函数必须与基类虚函数具有相同的签名(函数名、参数列表、返回类型)。
- 虚函数表(vtable):编译器为每个含虚函数的类生成一个隐藏的指针数组,存储虚函数地址。
语法与示例[编辑 | 编辑源代码]
基础语法[编辑 | 编辑源代码]
class Base {
public:
virtual void show() {
std::cout << "Base class show()" << std::endl;
}
};
class Derived : public Base {
public:
void show() override { // override关键字(C++11)显式声明重写
std::cout << "Derived class show()" << std::endl;
}
};
int main() {
Base* b = new Derived();
b->show(); // 输出 "Derived class show()"
delete b;
return 0;
}
输出:
Derived class show()
纯虚函数与抽象类[编辑 | 编辑源代码]
虚函数可声明为纯虚函数(Pure Virtual),使类成为抽象类(不能实例化):
class AbstractBase {
public:
virtual void pureVirtual() = 0; // 纯虚函数
};
虚函数表(vtable)原理[编辑 | 编辑源代码]
编译器为每个含虚函数的类生成一个虚函数表,存储指向实际函数的指针。对象内部包含一个指向vtable的指针(vptr)。
实际应用案例[编辑 | 编辑源代码]
场景:图形渲染系统[编辑 | 编辑源代码]
通过基类Shape
的虚函数draw()
实现多态渲染:
class Shape {
public:
virtual void draw() const = 0;
};
class Circle : public Shape {
public:
void draw() const override {
std::cout << "Drawing a circle" << std::endl;
}
};
class Square : public Shape {
public:
void draw() const override {
std::cout << "Drawing a square" << std::endl;
}
};
void renderShapes(const std::vector<Shape*>& shapes) {
for (const auto& shape : shapes) {
shape->draw(); // 动态绑定调用具体实现
}
}
常见问题与注意事项[编辑 | 编辑源代码]
1. 性能开销:虚函数调用比普通函数多一次间接寻址(通过vtable)。 2. 构造函数/析构函数中调用虚函数:此时虚机制未生效,会调用当前类的版本。 3. 默认参数:虚函数的默认参数在编译期确定,建议避免使用。
进阶话题[编辑 | 编辑源代码]
- 协变返回类型:派生类虚函数可返回基类虚函数返回类型的派生类。
- 动态类型识别:结合
typeid
和dynamic_cast
实现类型安全操作。
协变返回类型示例[编辑 | 编辑源代码]
class Base {
public:
virtual Base* clone() const { return new Base(*this); }
};
class Derived : public Base {
public:
Derived* clone() const override { // 返回类型为Derived*
return new Derived(*this);
}
};
总结[编辑 | 编辑源代码]
虚函数是C++多态性的基石,通过动态绑定实现灵活的对象行为。理解其底层机制(如vtable)有助于优化性能并避免常见错误。