不带指针或引用的c++虚函数调用

c++ virtual function call without pointer or reference

本文关键字:c++ 函数调用 引用 指针      更新时间:2023-10-16

据我所知,虚函数调用通常需要指针或引用。所以我对下面的代码感到非常惊讶。

#include <iostream>
using namespace std;
class B{
public:
  void runB(){        call();  }
  virtual void call(){ cout<<"Bn"; };
};
class D: public B{
public:
  void runD(){       runB();   }
  void call(){       cout<<"Dn";  }
};
int main(){
 D d;
 d.runD();
}

输出为

D

有人能评论一下为什么这个虚函数调用是有效的吗?谢谢。

在成员函数中,任何对其他成员函数或变量的引用都通过this指针隐式解析。在runB()的定义中,call()实际上是指this->call()。虚函数调用使用当前对象的虚表执行。

首先,虚函数调用不需要指针或引用。就语言而言,任何对虚函数的调用都是虚调用,除非通过使用限定函数名显式地抑制虚分派机制。例如,这些

d.D::call(); // calls `D::call()` directly
d.B::call(); // calls `B::call()` directly

是显式强制为非虚的调用。然而,这

d.call(); // calls `D::call()` virtually

是虚调用。在这种情况下,对编译器来说,目标函数是D::call()是显而易见的,因此编译器通常会将这个虚拟调用优化为常规的直接调用。然而,从概念上讲,d.call()仍然是一个虚拟调用。

其次,在B::runB()内部调用call()是通过指针进行的。指针隐式地出现在这里。在B::runB()里面写call()只是(*this).call()的简写。this是指针。所以调用是通过一个指针进行的

第三,虚调用的关键属性是根据调用中使用的对象的动态类型选择目标函数。在您的例子中,即使在B::runB()内部,对象*this的动态类型也是D。这就是为什么它调用D::call(),因为它应该。

第四,真正需要指针或引用的是观察实际的多态性。当调用中使用的对象表达式的静态类型与其动态类型不同时,就会发生多态。为此,你确实需要一个指针或引用。这正是你在B::runB()内部的(*this).call()调用中观察到的。尽管*this的静态类型是B,但它的动态类型是D,并且调用被分派到D::call()

虚拟与非虚拟的区别是:

不是虚的——总是按照调用者对象/引用/指针类型。

virtual - reference/pointer -按创建的对象类型执行。

virtual - object -由调用者处理。

例如:

class A{
public:
    virtual void f(){
        cout <<"An";
    }
};
class B: public A{
public:
    virtual void f(){
        cout <<"Bn";
    }
};

B b;
A a,*pa=&b;
a.f(); //A: caller type = created type - same for not virtual
b.f(); //B: caller type = created type - same for not virtual
((A)b).f(); //A: object goes by the caller type - same for not virtual
pa->f(); // B: pointer goes by the created type - it would be A if it was not virtual!!