假设有这样一个抽象类:
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
?
也就是说,编译器是否可以只在表中找到一次函数并在另外两次使用它的地址?
附言从共享库加载实例只是一个例子,排除内联函数的可能性。
一般来说,你不能保证编译器会优化虚函数表查找只发生一次。依赖特定的编译器优化性能关键代码通常不是一个好主意。作为一个简单的解决方法,也许你可以这样做:
if (flag1 || flag2 || flag3) {
x.f();
}
但是我不知道你用这三个标志的目的或意义。
在 C 和 C++ 中有 AS IF 规则,因此编译器可以进行您期望的优化(如果启用)。问题是您没有保证编译器会这样做。要查看它是否发生,您必须检查汇编输出,这不是很好。
即使这样做:
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();
}
}
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