Early and Late Binding

What is the difference between Early and Late Binding?

The short answer is that early (or static) binding refers to compile time binding and late (or dynamic) binding refers to runtime binding (for example when you use reflection).

What is early (static) and late (dynamic) binding in C++?

You read right. The basic example can be given with:

using FuncType = int(*)(int,int); // pointer to a function
// taking 2 ints and returning one.

int add(int a, int b) { return a + b; }
int substract(int a, int b) { return a - b; }

Static binding is when binding is known at compile time:

int main() {
std::cout << add(4, 5) << "\n";
}

leaves no room for a dynamic change of the operation, and thus is statically bound.

int main() {
char op = 0;
std::cin >> op;

FuncType const function = op == '+' ? &add : &substract;

std::cout << function(4, 5) << "\n";
}

whereas here, depending on the input, one gets either 9 or -1. This is dynamically bound.

Furthermore, in object oriented languages, virtual functions can be used to dynamically bind something. A more verbose example could thus be:

struct Function {
virtual ~Function() {}
virtual int doit(int, int) const = 0;
};
struct Add: Function {
virtual int doit(int a, int b) const override { return a + b; }
};
struct Substract: Function {
virtual int doit(int a, int b) const override { return a - b; }
};

int main() {
char op = 0;
std::cin >> op;

std::unique_ptr<Function> func =
op == '+' ? std::unique_ptr<Function>{new Add{}}
: std::unique_ptr<Function>{new Substract{}};

std::cout << func->doit(4, 5) << "\n";
}

which is semantically equivalent to the previous example... but introduces late binding by virtual function which is common in object-oriented programming.

Early and late binding + polymorphism in c++

Now how can it call the display function of the derived class without using the virtual keyword(late binding) before the display function in the base class?

A non-virtual function is simply resolved by the compiler, according to the static type of the object (or reference or pointer) it's called on. So given an object of the derived type, and a reference to its sub-object:

B b;
A & a = b;

you'd get different results from calling a non-virtual function:

b.display();  // called as B
a.display(); // called as A

If you know the real type, then you could specify that you want to call that version:

static_cast<B&>(a).display();  // called as B

but that would go horribly wrong if the object that a refers to doesn't have type B.

Now if we use Polymorphism and want to display the member function of the derived class if it is called we have to add the virtual keyword before display function in the base.

Correct. If you make the function virtual, then it's resolved at runtime according to the dynamic type of the object, even if you use a different type of reference or pointer to access it. So both the above examples would call it as B.

If we pass the object value by call by pointer and call by reference it call the function in the derived class but if we pass the object by value it doesn't why is it so?

If you pass it by value, then you're slicing it: copying just the A part of the object, to make a new object of type A. So, whether or not the function is virtual, calling it on that object would choose the A version, since it's an A and nothing but an A.

If you pass by reference or pointer, then you're still accessing the original object, with its dynamic type B. So the virtual function call would resolve to the B version.

C++ early binding and late binding

Here's what gcc and Clang produce for your code as it stands right now:

main:                                   # @main
xor eax, eax
ret

code on Godbolt

So in this case, we don't really have either early or late binding. Rather, we have no binding to the function at all--you didn't use the result you got from calling the function (either directly or via a pointer), so the compiler simply didn't generate any code to call the function at all.

We can repair that with code on this order:

#include <iostream>

int add (int x, int y)
{
return x+y;
}

int main()
{
int a=add(5,6);//early binding
int (*p_add)(int,int)=add;

int b=p_add(5,19);
std::cout << b;
}

In this case, the compiler still detect that the result of the function doesn't depend on anything at compile time, so it computes the value at compile time, and prints it out as a constant:

mov esi, 24
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)ant:

Code on Godbolt

So, we still don't have any real "binding" to the function. Let's have it use inputs it won't know until run-time:

#include <iostream>
#include <cstdlib>

int add (int x, int y)
{
return x+y;
}

int main()
{
int x1 = rand();
int x2 = rand();

int a=add(x1, x2);//early binding
int (*p_add)(int,int)=add;

int b=p_add(x1,x2);
std::cout << b;
}

This source produces the following object code:

call rand
mov ebx, eax
call rand
mov edi, OFFSET FLAT:_ZSt4cout
lea esi, [rbx+rax]
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)

The compiler is still aware that the pointer consistently points to one particular function, so even though the source code shows the function being called via a pointer, in the object code we don't call the function via a pointer...and in fact, we still don't call the function at all. Instead, the code for the body of the function has been generated inline.

To get an actual function call via a pointer, we can have a pointer that refers to either of two different functions, and it won't be apparent until run-time which of the two to use in a particular case. For example:

#include <iostream>
#include <cstdlib>

int add (int x, int y)
{
return x+y;
}

int sub(int x, int y) {
return x-y;
}

int main()
{
int x1 = rand();
int x2 = rand();

int z = rand() % 2;

int (*p_add)(int,int) = z ? add : sub;

int b=p_add(x1,x2);
std::cout << b;
}

This (finally!) make the call via a pointer actually happen as a call via a pointer:

  call rand
mov edx, OFFSET FLAT:sub(int, int) ; start by assuming we'll subract
mov esi, r12d
mov edi, ebp
test al, 1 ; then see if we have an odd or even number
mov eax, OFFSET FLAT:add(int, int)
cmove rax, rdx ; if necessary, point to add
call rax ; and finally call the function via the pointer
mov edi, OFFSET FLAT:_ZSt4cout
mov esi, eax
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)

Code on Godbolt

Summary

If it's apparent at compile time what function will be called, the compiler probably won't generate code to call the function via a pointer, even if that's what the source code shows.

What is the difference between Binding and Dispatching in Java?

I believe the confusion typically comes from how overloaded these terms are.

We program our programs in a high level language, and either a compiler or an interpreter must transform that into something a machine actually understands.

In coarse terms, you can picture a compiler transforming our method code into some form of machine code. If the compiler knew at that point exactly where in the memory that method would reside when we run our program later, then it could safely go and find every method invocation of this compiled method and replace it with a jump to this address where the compiled code resides, right?.

Well, materializing this relationship is what I understand as binding. This binding, though, could happen at different moments, for example at compile time, linking time, load time, or at run time depending on the design of the language.

The terms static and dynamic are generally used to refer to things bound before run time and at run time, respectively.

Later binding times are associated with greater flexibility, earlier binding times are associated with greater efficiency. Language designers have to balance these two aspects when they're creating a language.

Most object-oriented programming languages support subtype polymorphism. In these languages, virtual methods are bound at runtime depending on the dynamic type of the object at that point. In other words, virtual method invocations are dispatched to the appropriate implementation at runtime based on the dynamic type of the object implementation involved and not based solely on its static type reference.

So, in my opinion, you must first bind the method invocation to a specific implementation or execution address, and then you can dispatch an invocation to it.

I had answered a very similar question in the past in which I demonstrate with examples how this happens in Java.

I would also recommend reading the book Programming Language Pragmatics. It is a great reference to learn all this kind of stuff from a theoretical standpoint.

What are the advantages of late binding? Give one example in context of function pointers in C++

OK, I'm going to skip over the whole "early binding vs late binding" definition question and pretend you asked "why would someone use function pointers instead of a switch statement?"

Because function pointers are more flexible. They aren't static. Let's take the business end of your code:

int InvokeOperation(int nOperation, int nX, int nY)
{
switch (nOperation)
{
case 0: return Add(nX, nY);
case 1: return Subtract(nX, nY);
case 2: return Multiply(nX, nY);
}
}

That's nice and functional. But it's not flexible. Why? Because all of the functions that can be called are defined by InvokeOperation; if you want to add a new operation, you have to be able to change InvokeOperation.

By contrast, if you used function pointers, you can build a whole registry of operations:

using Func = int(*)(int, int);
struct Op{Func func; std::string name;};
std::vector<Func> funcs =
{
{&Add, "Add"},
{&Subtract, "Subtract"},
{&Multiply, "Multiply"},
};

int InvokeOperation(int nOperation, int nX, int nY)
{
return funcs[nOperation].func(nX, nY);
}

Now, if you want to add more operations, just insert elements into funcs. If InvokeOperation were part of some library, you wouldn't necessarily have the right to change it. With static binding, you would have an inflexible system; what it supports is what it will always support.

With dynamic binding, you can add whatever stuff you want, whether you have the right to modify the library directly or not.



Related Topics



Leave a reply



Submit