虚拟成员函数

成员函数也可以声明为 virtual 在这种情况下,如果调用指针或对实例的引用,则不会直接访问它们; 相反,他们将查找虚函数表中的函数(虚函数的指向成员函数的列表,通常称为 vtablevftable),并使用它来调用适合实例动态的版本(实际)类型。如果直接从类的变量调用函数,则不执行查找。

struct Base {
    virtual void func() { std::cout << "In Base." << std::endl; }
};

struct Derived : Base {
    void func() override { std::cout << "In Derived." << std::endl; }
};

void slicer(Base x) { x.func(); }

// ...

Base b;
Derived d;

Base *pb = &b, *pd = &d; // Pointers.
Base &rb = b, &rd = d;   // References.

b.func();   // Output:  In Base.
d.func();   // Output:  In Derived.

pb->func(); // Output:  In Base.
pd->func(); // Output:  In Derived.

rb.func();  // Output:  In Base.
rd.func();  // Output:  In Derived.

slicer(b);  // Output:  In Base.
slicer(d);  // Output:  In Base.

请注意,虽然 pdBase*,而 rdBase&,但是在两个叫 Derived::func() 而不是 Base::func() 中的任何一个上调用 func(); 这是因为 Derivedvtable 更新了 Base::func() 条目,而不是指向 Derived::func()。相反,请注意如何将实例传递给 slicer() 总是会导致 Base::func() 被调用,即使传递的实例是 Derived; 这是因为有一些称为数据切片的东西,其中通过值将 Derived 实例传递到 Base 参数会使得 Derived 实例中不属于 Base 实例的部分无法访问。

当成员函数定义为 virtual 时,所有具有相同签名的派生类成员函数都会覆盖它,无论覆盖函数是否指定为 virtual。然而,这可能使得派生类更难以被程序员解析,因为没有关于哪些函数是什么的指示 23。

struct B {
    virtual void f() {}
};

struct D : B {
    void f() {} // Implicitly virtual, overrides B::f.
                //  You'd have to check B to know that, though.
};

但是请注意,派生函数只有在其签名匹配时才会覆盖基函数; 即使派生函数明确声明为 virtual,如果签名不匹配,它也会创建一个新的虚函数。

struct BadB {
    virtual void f() {}
};

struct BadD : BadB {
    virtual void f(int i) {} // Does NOT override BadB::f.
};

Version >= C++ 11

从 C++ 11 开始,可以使用上下文相关关键字 override 明确覆盖意图。这告诉编译器程序员希望它覆盖基类函数,这会导致编译器在没有覆盖任何内容时省略错误。

struct CPP11B {
    virtual void f() {}
};

struct CPP11D : CPP11B {
    void f() override {}
    void f(int i) override {} // Error: Doesn't actually override anything.
};

这也有利于告诉程序员该函数既是虚拟的,又在至少一个基类中声明,这可以使复杂的类更容易解析。

当一个函数声明为 virtual,并在类定义之外定义时,virtual 说明符必须包含在函数声明中,而不是在定义中重复。

Version >= C++ 11

这也适用于 override

struct VB {
    virtual void f(); // "virtual" goes here.
    void g();
};
/* virtual */ void VB::f() {} // Not here.
virtual void VB::g() {} // Error.

如果基类重载 virtual 函数,则只有明确指定为 virtual 的重载才是虚拟的。

struct BOverload {
    virtual void func() {}
    void func(int) {}
};

struct DOverload : BOverload {
    void func() override {}
    void func(int) {}
};

// ...

BOverload* bo = new DOverload;
bo->func(); // Calls DOverload::func().
bo->func(1); // Calls BOverload::func(int).

有关更多信息,请参阅相关主题