虚函数(virtual function)

来源:互联网 发布:淘宝刷单兼职91lingla 编辑:程序博客网 时间:2024/06/01 08:11

  在C++中,当我们使用基类的引用或指针调用一个虚成员函数时,会执行动态绑定。因为我们直到运行时才能知道到底调用了哪个版本的虚函数,所以所有虚函数必须都有定义。通常情况下,如果我们不使用某个函数,则不需要为该函数提供定义。但是,我们必须为没一个虚函数都提供定义,而不管它是否被用到。

对虚函数的调用可能在运行时才被解析

  当某个虚函数通过引用或指针被调用时,编译器产生的代码知道运行时才能确定应该调用哪个版本的函数。被调用的函数是与绑定到引用或指针上的对象的动态类型相匹配的那一个。
  动态绑定只有当我们通过引用或指针调用虚函数时才会发生。

派生类中的虚函数

  当在派生类中覆盖了某个虚函数时,可以再一次使用virtual关键字指出该函数的性质。然而这么做并非必须的,因为一旦某个函数被声明成虚函数,则在所有的派生类中它都是虚函数。
  一个派生类的函数如果覆盖了某个继承而来的虚函数,则它的形参类型必须与被它覆盖的基类函数完全一致
  同样,派生类中虚函数的返回值类型也必须与基类函数匹配。该规则存在一个例外,当类的虚函数返回类型是类本身的指针或者引用时,上述规则无效。也就是说,如果D由B派生得到,则基类的虚函数可以返回B*而派生类的对应函数可以返回D*,只不过这样的返回类型要求D到B的类型转换是可访问的

final和override说明符

  派生类如果定义了一个函数与基类虚函数的名字相同但形参列表不同,这仍然是合法的行为。编译器认为新定义的这个函数与基类中原有的函数是相互独立的。这时,派生类的函数并没有覆盖掉基类中的版本就实际的编程习惯而言,这种声明往往意味着发生了错误,因为我们原本可能希望派生类能覆盖基类中的虚函数。
  在C++11中,可以使用override关键字来说明派生类中的虚函数。这么做的好处是,在使得程序员的意图更加清晰的同时,让编译器可以为我们发现一些错误。如果我们用override关键字被标记了某个函数,但该函数并没有覆盖已存在的虚函数,此时编译器将报错。
  

struct B{    virtual void f1(int) const;    virtual void f2();    void f3();};struct D1:B{    void f1(const) const override;//正确,f1与基类中的f1匹配    void f2(int) override;  //错误,B没有形如f2(int)的函数    void f3() override; //错误,f3不是虚函数    void f4() override; //错误,B没有名为f4的函数}

  我们还能把某个函数指定为final,如果我们已经把函数定义成final,则之后任何尝试覆盖该函数的操作都将引发错误

struct D2:B{    void f1(int) const final;};struct D3:D2{    void f2();    void f1(int) const;//错误,D2已经将f2声明为final}

final和override说明符出现在形参列表(包括任何const或引用修饰符)以及尾置返回类型之后

虚函数与默认实参

  虚函数也可以拥有默认实参如果某次函数调用使用了默认实参,则该实参值由本次调用的静态类型确定
  换句话说,如果我们通过基类的指针或引用调用函数,则使用基类中定义的默认实参,即使实际运行的是派生类中的函数版本也是如此。此时,传入派生类函数的将是基类函数定义的默认实参。如果派生类函数以来不同的实参,则程序结果会与预期不一致。
  

如果虚函数使用默认实参,则基类和派生类中定义的默认实参最好一致。

回避虚函数的机制

  使用作用域运算符,可以不对虚函数的调用进行动态绑定,强迫其执行虚函数的某个特定版本。
  通常,当一个派生类的虚函数调用它覆盖的基类的虚函数版本时,需要回避虚函数的默认机制。在这种情况下,基类的版本通常完成继承层次中所有类型都要做的共同任务,而派生类中定义的版本需要执行一些与派生类密切相关的操作。

0 0
原创粉丝点击