1. 基类指针调用子类函数的核心机制
当我们在C++中使用基类指针指向子类对象时,函数调用的具体行为取决于该函数是否被声明为virtual。这是C++多态性的基础实现方式,也是面向对象编程中最容易产生困惑的点之一。
1.1 虚函数表的工作原理
每个包含虚函数的类都会有一个虚函数表(vtable),这是一个在编译时创建的静态数组,存储了指向该类虚函数的指针。当对象被创建时,编译器会在对象的内存布局中添加一个隐藏的vptr指针,指向对应的虚函数表。
cpp复制class Base {
public:
virtual void show() { cout << "Base show" << endl; }
virtual ~Base() {}
};
class Derived : public Base {
public:
void show() override { cout << "Derived show" << endl; }
};
在这种情况下,Base类和Derived类各自有自己的虚函数表。当我们通过基类指针调用虚函数时,实际执行的是vptr指向的虚函数表中对应的函数地址。
1.2 动态绑定的实现条件
动态绑定需要同时满足三个条件:
- 必须通过指针或引用调用
- 调用的是虚函数
- 在继承体系中有函数重写(override)
如果缺少任何一个条件,就会发生静态绑定,即根据指针的静态类型决定调用哪个函数。
重要提示:构造函数不能是虚函数,因为构造对象时需要知道确切类型。而析构函数通常应该声明为虚函数,特别是在有继承关系的情况下,否则可能造成资源泄漏。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 不同调用场景的详细分析
2.1 纯虚函数与抽象基类
当基类中的虚函数被声明为纯虚函数时,这个类就变成了抽象基类,不能直接实例化:
cpp复制class AbstractBase {
public:
virtual void pureVirtual() = 0; // 纯虚函数
virtual ~AbstractBase() {}
};
子类必须实现所有的纯虚函数才能被实例化。这种机制强制子类提供特定接口的实现,是实现多态的强大工具。
