2

I understand the difference between static and dynamic binding in the sense that method calls are determined at compile time for static binding - whereas method calls are determined at run time for dynamic binding.

One thing I don't get is why you have to pass by reference or pointer for dynamic binding. I tried looking online but I am still confused. Is it because when you pass by value, you are passing a copy which means it has to be initialised which means it gets sliced?

For example, Pet is a base class and Dog is a derived class.

Now...

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()

If someone could explain this to me better it will really make me happy

Thanks

  • 3
    These things are orthogonal. There's no strong requirement to have reference parameters for _dynamic binding_. May be you're mixing something up with _dynamic polymorphism_. – πάντα ῥεῖ Jan 09 '16 at 09:13

2 Answers2

1

You are confusing a lot of things with call-by-value and call-by-reference.

void print(Pet p);

This is a declaring print to be a function returning void and taking one parameter of Pet by value named p. This is call-by-value.

void print(Pet &p);

This is a declaring print to be a function returning void and taking one parameter of Pet by reference named p. This is call-by-reference.

Paul Evans
  • 27,315
  • 3
  • 37
  • 54
  • The OP is not necessarily confusing things. The matters of by-value and by-reference are directly related to static x dynamic binding behavior. See my answer. – Leandro T. C. Melo Jan 09 '16 at 13:44
1

Your assumption is roughly correct, not for the print function itself though, but for Pet's member functions eventually called within it. The print function that uses pass-by-value takes a base Pet object. Therefore the compiler is free to bind the call statically (and a Dog object passed to it would get sliced).

But for your print function that uses pass-by-reference, the compiler must bind the call to eat dynamically because it doesn't know which exact object lives at that address. Take a look at the following code.

#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;
}

If you uncomment option 1, this the code that gets generated. Notice the call to eat is statically resolved.

__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.

However, if you now uncomment option 2, the compiler must perform an indirect call, bound at runtime.

__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.
Leandro T. C. Melo
  • 3,974
  • 22
  • 22