没有指针或引用的c ++虚函数调用

cha*_*hao 9 c++ virtual pointers reference

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

#include <iostream>
using namespace std;
class B{
public:
  void runB(){        call();  }
  virtual void call(){ cout<<"B\n"; };
};

class D: public B{
public:
  void runD(){       runB();   }
  void call(){       cout<<"D\n";  }
};

int main(){
 D d;
 d.runD();
}
Run Code Online (Sandbox Code Playgroud)

输出是

d

有人可以评论为什么这个虚函数调用工作?谢谢.

Wyz*_*a-- 12

在成员函数中,通过this指针隐式解析对其他成员函数或变量的任何引用.所以在定义中runB(),call()真的意味着this->call().使用当前对象的虚拟表执行虚函数调用.


AnT*_*AnT 8

首先,虚函数调用不需要指针或引用.就语言而言,除非您使用限定的函数名明确地禁止虚拟调度机制,否则对虚函数的任何调用都是虚拟调用.例如,这些

d.D::call(); // calls `D::call()` directly
d.B::call(); // calls `B::call()` directly
Run Code Online (Sandbox Code Playgroud)

是明确强制为非虚拟的呼叫.但是,这个

d.call(); // calls `D::call()` virtually
Run Code Online (Sandbox Code Playgroud)

是一个虚拟的电话.在这种情况下,编译器立即明白目标函数是D::call(),因此编译器通常将此虚拟调用优化为常规直接调用.然而,从概念上讲,d.call()它仍然是一个虚拟的呼叫.

其次,对call()内部的调用B::runB()是通过指针进行的.指针隐含地出现在那里.call()在里面写作B::runB()只是一种简写(*this).call().this是一个指针.这样的呼叫通过指针做.

第三,虚拟调用的关键属性是根据调用中使用的对象的动态类型选择目标函数.在你的情况下,即使你是里面B::runB()的动态类型的对象*thisD.这就是D::call()它应该召唤的原因.

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


SHR*_*SHR 5

虚拟与非虚拟的区别在于:

不是虚拟的 - 总是由调用者对象/引用/指针类型决定。

虚拟 - 引用/指针 - 由创建的对象类型决定。

虚拟 - 对象 - 由调用者处理。

例如:

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


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!!
Run Code Online (Sandbox Code Playgroud)