2

我理解静态绑定和动态绑定之间的区别,因为静态绑定的方法调用是在编译时确定的,而动态绑定的方法调用是在运行时确定的。

我不明白的一件事是为什么您必须通过引用或指针传递动态绑定。我尝试在网上查找,但我仍然感到困惑。是不是因为当你按值传递时,你传递的是一个副本,这意味着它必须被初始化,这意味着它被切片?

例如,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()

如果有人能更好地向我解释这一点,那真的会让我开心

谢谢

4

2 回答 2

1

您将很多事情与call-by-valuecall-by-reference混淆了。

void print(Pet p);

这是一个声明print为一个函数,它返回void并采用一个名为Pet参数p。这是按值调用

void print(Pet &p);

这是一个声明print为一个函数,它返回void并获取一个名为Pet引用p参数。这是按引用调用

于 2016-01-09T09:44:50.170 回答
1

您的假设大致正确,但不是针对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.
于 2016-01-09T13:43:01.167 回答