c++ 针对同一对象上的同一函数的重复调用的虚拟函数优化

ryevplcw  于 2023-03-25  发布在  其他
关注(0)|答案(2)|浏览(110)

假设有这样一个抽象类:

class Base {
   public:

   virtual void f() = 0;

   virtual ~Base()  = default;
};

和一些功能:

void function (Base& x, bool flag1, bool flag2, bool flag3) {

   if(flag1)
      x.f();
   if(flag2)
      x.f();
   if(flag3)
      x.f();
}

main()函数中,我从共享库中加载了一个派生自该类的示例:

int main() {
  Base* x = /* load from shared lib*/;

  bool flag1 = getchar() == '1';
  bool flag2 = getchar() == '2';
  bool flag3 = getchar() == '3';

  function(*x, flag1, flag2, flag3);

  return 0;
}

问:我是否可以期望在一次调用函数void function (Base& x, bool flag1, bool flag2, bool flag3)的过程中,虚拟函数表只被访问一次,即使所有三个标志都是true?也就是说,编译器是否可以在表中只找到一次函数,并在其他两次中使用其地址?
P.s.从共享库加载示例只是一个例子,以排除内联函数的可能性。

gg58donl

gg58donl1#

即使这样做:

void function (Base& x, bool flag1, bool flag2, bool flag3) {
   if(flag1 || flag2 || flag3) {
      if(flag1)
         x.f();
      if(flag2)
         x.f();
      if(flag3)
         x.f();
   }
}

使用GCC -O3为每个调用加载vtable指针(mov rax, QWORD PTR [rbx]):

function(Base&, bool, bool, bool):
        push    r12
        mov     r12d, ecx
        push    rbp
        mov     ebp, edx
        push    rbx
        mov     rbx, rdi
        test    sil, sil
        jne     .L2
        test    dl, dl
        jne     .L2
.L6:
        test    r12b, r12b
        jne     .L12
.L9:
        pop     rbx
        pop     rbp
        pop     r12
        ret
.L2:
        mov     rax, QWORD PTR [rbx]
        mov     rdx, QWORD PTR [rax]
        test    sil, sil
        je      .L7
        mov     rdi, rbx
        call    rdx
        test    bpl, bpl
        je      .L6
        mov     rax, QWORD PTR [rbx]
.L7:
        mov     rdi, rbx
        call    [QWORD PTR [rax]]
        test    r12b, r12b
        je      .L9
.L12:
        mov     rax, QWORD PTR [rbx]
        mov     rdi, rbx
        pop     rbx
        pop     rbp
        pop     r12
        mov     rax, QWORD PTR [rax]
        jmp     rax
1bqhqjot

1bqhqjot2#

在C和C++中有AS IF rule,所以编译器可以做你期望的优化(如果它们被启用的话)。问题是你没有保证编译器会这样做。要看看它是否发生,你必须检查汇编输出,这并不好。
Here你可以看到所有的编译器都在使用call [QWORD PTR [rax]](或多或少),所以这是没有优化的。

相关问题