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.