首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

virtual function specifier

virtual说明符指定非静态成员函数是虚拟并支持动态绑定。它只能出现在非静态成员函数%28i的初始声明的decl-说明符-seq中。e.在类定义%29中声明它时。

解释

虚拟函数是可以在派生类中重写其行为的成员函数。相对于非虚拟函数,即使没有关于类的实际类型的编译时信息,重写的行为也会被保留。如果使用指针或对基类的引用处理派生类,则调用重写的虚拟函数将调用派生类中定义的行为。如果使用限定名查找%28,也就是说,如果函数%27s的名称出现在范围解析操作符的右侧::29%。

二次

代码语言:javascript
复制
#include <iostream>
struct Base {
   virtual void f() {
       std::cout << "base\n";
   }
};
struct Derived : Base {
    void f() override { // 'override' is optional
        std::cout << "derived\n";
    }
};
int main()
{
    Base b;
    Derived d;
 
    // virtual function call through reference
    Base& br = b; // the type of br is Base&
    Base& dr = d; // the type of dr is Base& as  well
    br.f(); // prints "base"
    dr.f(); // prints "derived"
 
    // virtual function call through pointer
    Base* bp = &b; // the type of bp is Base*
    Base* dp = &d; // the type of dp is Base* as  well
    bp->f(); // prints "base"
    dp->f(); // prints "derived"
 
    // non-virtual function call
    br.Base::f(); // prints "base"
    dr.Base::f(); // prints "base"
}

二次

详细

如果某个成员函数vf被宣布为virtual在课堂上Base,还有一些阶级Derived,直接或间接地从Base,具有相同的成员函数声明。

  • 名称
  • 参数类型列表%28,而不是返回类型%29
  • 简历-资格
  • 参考-限定符

那么这个函数在类中Derived也是虚拟%28是否关键字virtual在声明%29和覆盖基础::VF%28,不论单词是否override在声明%29中使用。

Base::vf不需要是可见的%28可以声明为私有,或者使用私有继承%29继承可以被重写。

二次

代码语言:javascript
复制
class B {
    virtual void do_f(); // private member
 public:
    void f() { do_f(); } // public interface
};
struct D : public B {
    void do_f() override; // overrides B::do_f
};
 
int main()
{
    D d;
    B* bp = &d;
    bp->f(); // internally calls D::do_f();
}

二次

对于每个虚拟函数,都有最终越野车,它在执行虚拟函数调用时执行。虚拟成员函数vf基类的Base是最后的重写器,除非派生类通过多重继承%29声明或继承%28,这是重写的另一个函数。vf...

二次

代码语言:javascript
复制
struct A { virtual void f(); };     // A::f is virtual
struct B : A { void f(); };         // B::f overrides A::f in B
struct C : virtual B { void f(); }; // C::f overrides A::f in C
struct D : virtual B {}; // D does not introduce an overrider, B::f is final in D
struct E : C, D  {       // E does not introduce an overrider, C::f is final in E
    using A::f; // not a function declaration, just makes A::f visible to lookup
};
int main() {
   E e;
   e.f();    // virtual call calls C::f, the final overrider in e
   e.E::f(); // non-virtual call calls A::f, which is visible in E
}

二次

如果一个函数有一个以上的最后过载者,程序是不正确的:

二次

代码语言:javascript
复制
struct A {
    virtual void f();
};
struct VB1 : virtual A {
    void f(); // overrides A::f
};
struct VB2 : virtual A {
    void f(); // overrides A::f
};
// struct Error : VB1, VB2 {
//     // Error: A::f has two final overriders in Error
// };
struct Okay : VB1, VB2 {
    void f(); // OK: this is the final overrider for A::f
};
struct VB1a : virtual A {}; // does not declare an overrider
struct Da : VB1a, VB2 {
    // in Da, the final overrider of A::f is VB2::f
};

二次

具有相同名称但不同参数列表的函数不会覆盖同名的基函数,但是生皮它:什么时候非限定名查找检查派生类的作用域,查找将查找声明而不检查基类。

二次

代码语言:javascript
复制
struct B {
    virtual void f();
};
struct D : B {
    void f(int); // D::f hides B::f (wrong parameter list)
};
struct D2 : D {
    void f(); // D2::f overrides B::f (doesn't matter that it's not visible)
};
 
int main()
{
    B b;   B& b_as_b   = b;
    D d;   B& d_as_b   = d;    D& d_as_d = d;
    D2 d2; B& d2_as_b  = d2;   D& d2_as_d = d2;
 
    b_as_b.f(); // calls B::f()
    d_as_b.f(); // calls B::f()
    d2_as_b.f(); // calls D2::f()
 
    d_as_d.f(); // Error: lookup in D finds only f(int)
    d2_as_d.f(); // Error: lookup in D finds only f(int)
}

二次

If a function is declared with the specifier override, but does not override a virtual function, the program is ill-formed: struct B { virtual void f(int); }; struct D : B { virtual void f(int) override; // OK, D::f(int) overrides B::f(int) virtual void f(long) override; // Error: f(long) does not override B::f(int) }; If a function is declared with the specifier final, and another function attempts to override it, the program is ill-formed. struct B { virtual void f() const final; }; struct D : B { void f() const; // Error: D::f attempts to override final B::f };

(since C++11)

非成员函数和静态成员函数不能是虚拟的。

不能声明函数模板virtual这仅适用于本身为模板的函数--类模板的常规成员函数可以声明为虚拟函数。

Virtual functions cannot have any associated constraints. struct A { virtual void f() requires true; // Error: constrained virtual function };

(concepts TS)

默认参数在编译时替换虚拟函数。

协变量返回类型

如果函数Derived::f重写函数Base::f,则它们的返回类型必须是相同的,或者是协变.两种类型是共变的,如果它们满足下列所有要求:

  • 这两种类型都是指向类的指针或引用%28 lvalue或rvalue%29。不允许多级指针或引用。
  • 返回类型中的引用/指向类。Base::f()必须是返回类型的引用/指向类的明确和可访问的直接或间接基类。Derived::f()...
  • 返回类型Derived::f()必须是平等的或更少的简历-合格的返回类型Base::f()...

的返回类型中的类。Derived::f一定是Derived本身,或必须是完全型在声明点Derived::f...

当进行虚拟函数调用时,最终覆盖程序返回的类型为隐式转换被调用的重写函数的返回类型:

二次

代码语言:javascript
复制
class B {};
 
struct Base {
    virtual void vf1();
    virtual void vf2();
    virtual void vf3();
    virtual B* vf4();
    virtual B* vf5();
};
 
class D : private B {
    friend struct Derived; // in Derived, B is an accessible base of D
};
 
class A; // forward-declared class is an incomplete type
 
struct Derived : public Base {
    void vf1();    // virtual, overrides Base::vf1()
    void vf2(int); // non-virtual, hides Base::vf2()
//  char vf3();    // Error: overrides Base::vf3, but has different
                   // and non-covariant return type
    D* vf4();      // overrides Base::vf4() and has covariant return type
//  A* vf5();      // Error: A is incomplete type
};
 
int main()
{
    Derived d;
    Base& br = d;
    Derived& dr = d;
 
    br.vf1(); // calls Derived::vf1()
    br.vf2(); // calls Base::vf2()
//  dr.vf2(); // Error: vf2(int) hides vf2()
 
    B* p = br.vf4(); // calls Derived::vf4() and converts the result to B*
    D* q = dr.vf4(); // calls Derived::vf4() and does not convert
                     //  the result to B*
 
}

二次

虚拟析构器

即使析构函数不被继承,如果基类声明其析构函数virtual,派生的析构函数总是重写它。这样就可以通过指向基的指针删除动态分配的多态类型对象。

二次

代码语言:javascript
复制
class Base {
 public:
    virtual ~Base() { /* releases Base's resources */ }
};
 
class Derived : public Base {
    ~Derived() { /* releases Derived's resources */ }
};
 
int main()
{
    Base* b = new Derived;
    delete b; // Makes a virtual function call to Base::~Base()
              // since it is virtual, it calls Derived::~Derived() which can
              // release resources of the derived class, and then calls
              // Base::~Base() following the usual order of destruction
}

二次

此外,如果一个类是多态%28声明或继承至少一个虚拟函数%29,其析构函数不是虚拟的,删除它是未定义行为无论是否存在资源泄漏,如果派生析构函数没有被调用。

一个有用的指导原则是,任何基类的析构函数必须是公共和虚拟或受保护和非虚拟的...

在建造和破坏过程中

当虚拟函数直接或间接地从构造函数或析构函数%28调用时,包括在类的非静态数据成员的构造或销毁期间,例如在成员中。初始化程序列表%29,并且调用的对象是正在构造或销毁的对象,调用的函数是构造函数或析构函数类中的最后一个覆盖器,而不是在一个更派生的类中重写它的函数。换句话说,在构建或破坏过程中,更多派生类并不存在.

当构造一个包含多个分支的复杂类时,在属于一个分支的构造函数中,多态性仅限于该类及其基:如果它获得一个指针或对该子层次结构之外的基子对象的引用,并试图调用一个虚拟函数调用%28e.g。使用显式成员访问%29,该行为未定义:

二次

代码语言:javascript
复制
struct V {
    virtual void f();
    virtual void g();
};
 
struct A : virtual V {
    virtual void f(); // A::f is the final overrider of V::f in A
};
struct B : virtual V {
    virtual void g(); // B::g is the final overrider of V::g in B
    B(V*, A*);
};
struct D : A, B {
    virtual void f(); // D::f is the final overrider of V::f in D
    virtual void g(); // D::g is the final overrider of V::g in D
 
    // note: A is initialized before B
    D() : B((A*)this, this) 
    {
    }
};
 
// the constructor of B, called from the constructor of D 
B::B(V* v, A* a)
{
    f(); // virtual call to V::f (although D has the final overrider, D doesn't exist)
    g(); // virtual call to B::g, which is the final overrider in B 
 
    v->g(); // v's type V is base of B, virtual call calls B::g as before
 
    a->f(); // a’s type A is not a base of B. it belongs to a different branch of the
            // hierarchy. Attempting a virtual call through that branch causes
            // undefined behavior even though A was already fully constructed in this
            // case (it was constructed before B since it appears before B in the list
            // of the bases of D). In practice, the virtual call to A::f will be
            // attempted using B's virtual member function table, since that's what
            // is active during B's construction)
}

二次

另见

  • 派生类和继承模式
  • 覆盖说明符%28自C++11%29
  • 最终说明符%28自C++11%29
代码语言:txt
复制
 ? cppreference.com

在CreativeCommonsAttribution下授权-ShareAlike未移植许可v3.0。

扫码关注腾讯云开发者

领取腾讯云代金券

http://www.vxiaotou.com