现代C++学习—— final 优化虚函数调用

我们都知道,在C++中,虚函数的调用是比较耗时的一种操作。

首先需要查询虚表,然后跳转,调用。

实际上,影响虚函数速度的关键是不能进行内联优化

这里我们会写一些UB的代码,来查看,是否调用了虚函数。

这里你可能需要严格鸽:C++虚函数表的位置——从内存的角度

我们看一下以下代码输出什么

#include <iostream>
struct A {
public:
  virtual void foo() { std::cout << "A \n"; }
  int x, y;
};
struct B : A{
   void foo() override {std::cout<<"B \n";};
};
struct C : A{
public:
  virtual void foo() { std::cout << "C \n"; }
};
int main() {
  using u64 = unsigned long long;
  A *a = new A{};
  {
    a->foo(); 
  }
  C * c = new C{};
  {
    *(u64 *)a = *(u64 *)c;
    a->foo();
  } 
  {
    A aa  = * a;
    aa.foo();
  }
  {
    A &aa = *a;
    aa.foo();
  }
  return 0;
}

可以思考一下

实际上,输出结果为

A

C

A

C

Compiler Explorer - C++

我们来解释一下

第一次输出A,这个很简单,就是函数调用。

接下来这个句话

*(u64 *)a =*(u64 *)c;

他是把,a的虚表,换成了c的。

接下来再调用foo,就是c的foo了。

所以第二次输出是C

第三次,第四次输出,需要知道,只有指针和引用才有多态,才会去查虚表。

也就是

A aa =* a; aa.foo();

是不需要查虚表,所以输出A,而引用要,但我们改了虚表,所以是C

再看另外一份代码

#include <iostream>
struct A {
public:
  virtual void foo() { std::cout << "A \n"; }
  int x, y;
};
struct B : A{
   void foo() override {std::cout<<"B \n";};
};
struct C : A{
public:
  virtual void foo() { std::cout << "C \n"; }
};
int main() {
  using u64 = unsigned long long;
  A *a = new B{};
  C * c = new C{};
  *(u64 *)a = *(u64 *)c;
  a->foo();
  static_cast<B*>(a)->foo();
  B * b = static_cast<B*>(a);
  b->foo();
  return 0;
}

输出

C

C

C

Compiler Explorer - C++

感觉和上面很像?

但是如果我们稍微改一下,给B加个final

#include <iostream>
struct A {
public:
  virtual void foo() { std::cout << "A \n"; }
  int x, y;
};
struct B final : A{
   void foo() override {std::cout<<"B \n";};
};
struct C : A{
public:
  virtual void foo() { std::cout << "C \n"; }
};
int main() {
  using u64 = unsigned long long;
  A *a = new B{};
  C * c = new C{};
  *(u64 *)a = *(u64 *)c;
  a->foo();
  static_cast<B*>(a)->foo();
  B * b = static_cast<B*>(a);
  b->foo();
  return 0;
}

输出就变成了

C

B

B

Compiler Explorer - C++

为什么呢?

这实际上是一个优化,考虑到虚函数的使用场景

一个类,有子类了,我用父类的指针指向了子类,在编译期不知道调用的是哪个函数。

但是如果用final修饰了呢?

我都没有子类了,所以B * 调用的函数是可以在编译期确定的,也就不需要查虚函数表了。

这种优化,叫做去虚拟化

https://quuxplusone.github.io/blog/2021/02/15/devirtualization/

全部评论
这个去虚拟化有东西,怪不得项目代码的集成都用带着final
点赞 回复 分享
发布于 2023-05-06 17:47 北京

相关推荐

03-27 17:33
门头沟学院 Java
代码飞升:同学院本,你要注意hr当天有没有回复过,早上投,还要打招呼要推销自己,不要一个劲投
点赞 评论 收藏
分享
评论
10
16
分享

创作者周榜

更多
牛客网
牛客企业服务