我理解静态绑定和动态绑定之间的区别,方法调用是在编译时为静态绑定确定的 - 而方法调用是在运行时确定的,用于动态绑定。
我不知道的一件事是你必须通过引用或指针传递动态绑定。我尝试在网上看,但我仍然感到困惑。是因为当你通过值传递时,你传递的是一个副本,这意味着它必须被初始化,这意味着它会被切片?
例如,Pet
是基类,Dog
是派生类。
现在...
void print(Pet p) {} // Calls print from the pet class
void print(Pet &p) {} // Calls print from the type of pet object being passed. For example, Dog::print() rather than Pet::print()
如果有人能够更好地向我解释,那真的会让我开心
由于
答案 0 :(得分:1)
您通过按值调用和按引用调用来混淆很多事情。
void print(Pet p);
这是一个声明print
的函数,它返回void
并将Pet
的一个参数改为 value ,名为p
。这是按值调用。
void print(Pet &p);
这是一个声明print
的函数,它返回void
并通过名为Pet
的引用获取p
的一个参数。这是按引用调用。
答案 1 :(得分:1)
您的假设大致正确,但不是print
函数本身,而是Pet
最终在其中调用的成员函数。使用按值传递的print
函数采用基础Pet
对象。因此,编译器可以自由地绑定调用静态(传递给它的Dog
对象将被切片)。
但对于使用 pass-by-reference 的print
函数,编译器必须将调用绑定到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,则会生成此代码。请注意,对eat的调用是静态已解决。
__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.