C++ Language Concepts

The Difference Between Inheritance and Overriding – Static Binding vs Dynamic Binding

doyyy_0 2025. 5. 21. 10:25

Inheritance is the concept where a child class inherits the member variables and member functions of its parent class.
While both inheritance and overriding are based on this concept of inheritance, overriding is a specific case where a child class redefines a virtual function from the parent class.

The key difference between the two lies in the distinction between static binding and dynamic binding.

 

When using inheritance alone, function calls are determined at compile time.
The function that gets called depends on the type of the pointer (whether it's Base* or Derived*).
This is known as static binding.

 

The code is as follows:

#include <iostream>
using namespace std;

class Base {
public:
    void SayHello() {
        cout << "Hello from Base\n";
    }
};

class Derived : public Base {
public:
    void SayHello() {
        cout << "Hello from Derived\n";
    }
};

int main() {
    Base* ptr = new Derived();
    ptr->SayHello();  // 출력: Hello from Base (static binding)
    delete ptr;
}



In contrast, overriding supports dynamic binding.
In this case, the compiler generates a structure called a vtable (virtual function table),
and each object holds a vptr, a pointer to the vtable corresponding to its class.
When a function is called, the vtable is referenced to determine at runtime which function matches the actual type of the object.

 

The code is as follows:

#include <iostream>
using namespace std;

class Base {
public:
    virtual void SayHello() {
        cout << "Hello from Base\n";
    }
};

class Derived : public Base {
public:
    void SayHello() override {
        cout << "Hello from Derived\n";
    }
};

int main() {
    Base* ptr = new Derived();
    ptr->SayHello();  // 출력: Hello from Derived (dynamic binding)
    delete ptr;
}

In this case, although the function is called through a Base* pointer, the actual object is of type Derived,
so Derived’s version of SayHello() is executed.


This is made possible by dynamic binding, where the function is resolved at runtime using the vtable.

One might wonder, “Why not just use a Derived* pointer from the start?”


However, in real-world applications, it's common to work with various objects through a shared interface (e.g., Base*).
For this reason, the pointer type is often unified to the parent type (Base*) to allow polymorphic behavior.

vector<Base*> objects;
objects.push_back(new Derived());
objects.push_back(new AnotherDerived()); 

 

Without dynamic binding in such situations, each class would have to be handled separately,
leading to code duplication and significantly increased maintenance complexity.