C++ Статическое и динамическое связывание

Я понимаю разницу между статическим и динамическим связыванием в том смысле, что вызовы методов определяются во время компиляции для статического связывания, тогда как вызовы методов определяются во время выполнения для динамического связывания.

Одна вещь, которую я не понимаю, это то, почему вы должны передавать по ссылке или указателю для динамического связывания. Я пытался смотреть онлайн, но я все еще в замешательстве. Это потому, что когда вы передаете по значению, вы передаете копию, что означает, что она должна быть инициализирована, что означает, что она будет разрезана?

Например, 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()

Если бы кто-то мог объяснить это мне лучше, это действительно сделает меня счастливым

Спасибо

2 ответа

Вы путаете много вещей с вызовом по значению и вызовом по ссылке.

void print(Pet p);

Это декларирование print быть возвращающей функцией void и принимая один параметр Pet по имени p, Это вызов по значению.

void print(Pet &p);

Это декларирование print быть возвращающей функцией void и принимая один параметр Pet по ссылке p, Это вызов по ссылке.

Ваше предположение примерно верно, не для print функционировать сам по себе, но для Petфункции-члены в конечном итоге вызываются внутри него. print функция, которая использует передачу по значению, берет базу Pet объект. Поэтому компилятор может связывать вызов статическиDog переданный объект будет разрезан).

Но для вашего 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, это будет сгенерированный код. Обратите внимание, что вызов для еды статически разрешен.

__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.
Другие вопросы по тегам