Я понимаю разницу между статическим и динамическим связыванием в том смысле, что вызовы методов определяются во время компиляции для статического связывания, тогда как вызовы методов определяются во время выполнения для динамического связывания.
Одна вещь, которую я не понимаю, это то, почему вы должны передавать по ссылке или указателю для динамического связывания. Я пытался смотреть онлайн, но я все еще в замешательстве. Это потому, что когда вы передаете по значению, вы передаете копию, что означает, что она должна быть инициализирована, что означает, что она будет разрезана?
Например, 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()
Если бы кто-то мог объяснить это мне лучше, это действительно сделает меня счастливым
Спасибо
Вы путаете много вещей с вызов по значению а также позвонить по ссылке.
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.