C++编译器能够优化同一指针上的重复虚函数调用吗?

16

假设我有以下代码

void f(PolymorphicType *p)
{
    for (int i = 0; i < 1000; ++i)
    {
        p->virtualMethod(something);
    }
}

编译器生成的代码会对pvtable条目进行1次还是1000次的解引用以调用virtualMethod?我正在使用Microsoft的编译器。

编辑

这是我关注的真实案例的汇编生成代码。 line->addPoint()是需要关注的虚拟方法。由于我没有汇编经验,所以我正在缓慢地查看它...

; 369  :        for (int i = 0; i < numPts; ++i)

    test    ebx, ebx
    je  SHORT $LN1@RDS_SCANNE
    lea edi, DWORD PTR [ecx+32]
    npad    2
$LL3@RDS_SCANNE:

; 370  :        {
; 371  :            double *pts = pPoints[i].SystemXYZ;
; 372  :            line->addPoint(pts[0], pts[1], pts[2]);

    fld QWORD PTR [edi+8]
    mov eax, DWORD PTR [esi]
    mov edx, DWORD PTR [eax+16]
    sub esp, 24                 ; 00000018H
    fstp    QWORD PTR [esp+16]
    mov ecx, esi
    fld QWORD PTR [edi]
    fstp    QWORD PTR [esp+8]
    fld QWORD PTR [edi-8]
    fstp    QWORD PTR [esp]
    call    edx
    add edi, 96                 ; 00000060H
    dec ebx
    jne SHORT $LL3@RDS_SCANNE
$LN314@RDS_SCANNE:

; 365  :        }

5
请要求编译器生成汇编代码并进行检查。 - Some programmer dude
1
使用优化编译并查看生成的代码。 - Anya Shenanigans
3
补充Joachim的评论 - 除了检查之外,没有其他方法可以找出答案。 - Bartek Banachewicz
2
好的,所以在循环内部是 mov edx, DWORD PTR [eax+16] ... call edx。我猜它每次都在查看虚函数表。 - japreiss
1
@jcoder 我同意。这个问题很容易回答(编译为汇编并检查)。更大的问题是是否标准允许这样的操作,是否完全涵盖,或者它是否放弃并完全由实现来决定。假设编译器发出的代码与标准提出的规则是同义词是一个伟大的想法,但对于像这样的问题,它似乎有点本末倒置。 - WhozCraig
显示剩余8条评论
2个回答

6
一般情况下是不可能的,因为该函数可能会销毁*this并在该空间中放置其他从相同基类派生的对象。
编辑:更简单地说,该函数可以仅仅改变p。编译器不可能知道谁拥有p的地址,除非它是局部于问题的优化单元。

2

一般情况下不可能,但是有些特殊情况可以进行优化,特别是在跨过程分析方面。使用VS2012的全面优化和整个程序优化编译此程序:

#include <iostream>

using namespace std;

namespace {
struct A {
  virtual void foo() { cout << "A::foo\n"; }
};

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

void test(A& a) {
  for (int i = 0; i < 100; ++i)
    a.foo();
}
}

int main() {
  B b;
  test(b);
}

to:

01251221  mov         esi,64h  
01251226  jmp         main+10h (01251230h)  
01251228  lea         esp,[esp]  
0125122F  nop  
01251230  mov         ecx,dword ptr ds:[1253044h]  
01251236  mov         edx,12531ACh  
0125123B  call        std::operator<<<std::char_traits<char> > (012516B0h)  
01251240  dec         esi  
01251241  jne         main+10h (01251230h)  

因此,它有效地优化了循环为:

for(int i = 0; i < 100; ++i)
  cout << "B::foo()\n";

对于整个程序/链接时优化来说,加1是非常有用的,因为它使得“不可能”的事情变得轻而易举。 - underscore_d

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接