您的假设大致正确,但不是针对print
函数本身,而是针对Pet
最终在其中调用的成员函数。print
使用按值传递的函数需要一个基础Pet
对象。因此,编译器可以自由地静态绑定调用(Dog
传递给它的对象会被切片)。
但是对于print
使用pass-by-reference的函数,编译器必须eat
动态绑定调用,因为它不知道哪个确切的对象位于该地址。看看下面的代码。
#include <iostream>
struct Pet {
virtual void eat() const { std::cout << "pet" << std::endl; }
};
struct Dog : Pet {
void eat() const /*override*/ { std::cout << "dog" << std::endl; }
};
// Option 1: void print(Pet p) { p.eat(); }
// Option 2: void print(Pet& p) { p.eat(); }
int main() {
Pet* p = new Dog;
print(*p);
delete p;
// In c++ 11 use override and std::unique_ptr.
return 0;
}
如果您取消注释选项 1,则会生成此代码。请注意,吃饭的电话是静态解决的。
__Z5print3Pet:
.cfi_startproc
pushq %rbp
Ltmp2:
.cfi_def_cfa_offset 16
Ltmp3:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp4:
.cfi_def_cfa_register %rbp
callq __ZNK3Pet3eatEv # HERE eat GETS CALLED.
但是,如果您现在取消注释选项 2,编译器必须执行间接调用,在运行时绑定。
__Z5printR3Pet:
.cfi_startproc
pushq %rbp
Ltmp2:
.cfi_def_cfa_offset 16
Ltmp3:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp4:
.cfi_def_cfa_register %rbp
subq $16, %rsp
movq %rdi, -8(%rbp)
movq -8(%rbp), %rdi
movq (%rdi), %rax
callq *(%rax) # HERE eat GETS INDIRECTLY CALLED.