Interview

The Difference Between Overloading and Overriding

Overloading and overriding are two concepts frequently tested in written exams and interviews. These two concepts differ from the concepts themselves to their …

Published

Overloading and overriding are two concepts frequently tested in written exams and interviews. These two concepts differ from the concepts themselves to their implementation mechanisms, but their forms of expression are similar. Starting from the implementation mechanisms, this article explains the differences between them in depth.

The Concepts of Overloading and Overriding #

Overloading mainly refers to function overloading or method overloading.

Function overloading or method overloading is a feature found in various programming languages such as Ada, C++, C#, D, and Java, that allows creating several methods with the same name which differ from each other in the type of the input and the output of the function. It is simply defined as the ability of one function to perform different tasks.

Overriding, by contrast, is a concept unique to object-oriented technology.

Method overriding, in object oriented programming, is a language feature that allows a subclass or child class to provide a specific implementation of a method that is already provided by one of its superclasses or parent classes. The implementation in the subclass overrides (replaces) the implementation in the superclass by providing a method that has same name, same parameters or signature, and same return type as the method in the parent class. The version of a method that is executed will be determined by the object that is used to invoke it. If an object of a parent class is used to invoke the method, then the version in the parent class will be executed, but if an object of the subclass is used to invoke the method, then the version in the child class will be executed. Some languages allow a programmer to prevent a method from being overridden.

How Overloading and Overriding Are Implemented #

This section mainly describes how overloading and overriding are implemented in C++. The primary reference is Chapter 3 of The Design and Evolution of C++.

Function overloading is determined at compile time. Several different functions can have the same name, and the compiler determines which function to call based on differences in the types of the call arguments. If it cannot determine the function based on differences in the call argument types, the compiler reports an error.

Method overriding is determined at runtime. In C++, the parent class marks functions that may be overridden as virtual functions. The subclass redefines the function by strictly using the same function name, parameter types, and return type as the function marked as virtual in the parent class; this is how method overriding is accomplished. At compile time, the compiler generates a virtual function pointer table, which stores the locations of the code for all virtual function definitions. At runtime, the location of the virtual function pointer table is determined through the virtual function table pointer, and then the location of the code for the called virtual function definition is determined.

Suppose we have the following code:

cpp
class A
{
public:
    void MA(int);
    void MB(double);
    virtual void MC(int, double);
private:
    int a;
    double b;
};

class B : public A
{
public:
    virtual void MC(int, double) override;
private:
    int c;
};

At this point, the compiler places the code for the three methods in class A and the code for the MC method in class B into the code segment, and then generates virtual function pointer tables for class A and class B, respectively. Depending on the allocation method, instances of class A or B are allocated on the stack or the heap. The memory layout of an instance of class A is typically as follows:

vptra
b

Here, vptr points to a table like this:

Address of the code for the MC method in A

The memory layout of an instance of class B is typically as follows:

a
b
vptrc

Here, vptr points to a table like this:

Address of the code for the MC method in B

A Common Type of Question in Written Exams and Interviews #

The concepts and implementation approaches of overloading and overriding are completely different, so why are they still frequently tested in written exams and interviews? The main reason is that their forms of expression are relatively similar, and there is also something called “function hiding” that causes confusion. “Function hiding” is not a good concept. In my view, “function hiding” should never be used at any time, and some newer languages also impose certain restrictions on “function hiding”. For example, in C#, the new keyword must be used as an annotation before “function hiding” can be used. I previously published an article discussing why “function hiding” is unnecessary: the original article is here.

First, let’s discuss function hiding. It is a special case of overloading. If a method is defined in a subclass with exactly the same function name, parameter types, and return type as a method in the parent class, and that method in the parent class is not marked as virtual, then function hiding occurs. In the subclass, special syntax must be used to call the method with the same name in the parent class. Because this is a special case of overloading, the decision made when calling this function still occurs at compile time.

Now consider a more complex example, which is also a type that is commonly tested in written exams:

cpp
class A
{
public:
    void MA(int);
    void MA(double);
    void MB(double);
    virtual void MC(int, double);
    virtual void MD(double);
};

class B : public A
{
public:
    void MA(double);
    void MB(int);
    void MD(double);
};

...

A a0;
A a1 = B();
B b;

a0.MA(0.5);
a1.MA(0.5);
b.MA(0.5);
a0.MB(0.5);
a1.MB(0.5);
b.MB(0.5);
a0.MD(0.5);
a1.MD(0.5);
b.MD(0.5);

Which functions do these calls invoke?

First, a0.MA(0.5) and a1.MA(0.5) certainly call void A::MA(double), because the MA method is not declared as a virtual function. Therefore, the method to call can be determined at compile time. Since when calling the MA method, both a0 and a1 claim that their type is A, the MA method in class A is called. b.MA(0.5), however, calls void B::MA(double). Because the method void MA(double) in the base class A is not declared as virtual, a static decision must be made. Since b claims that its type is B, the MA method in B is called.

Next, look at the MB group. Because there is only one function definition, void A::MB(double), all calls invoke this function.

Finally, look at the MD group. Because the base class A declares the MD method as a virtual function, a dynamic decision is required. The runtime type of a0 is A, so void A::MD(double) is called. The runtime type of a1 is B, so void B::MD(double) is called. The runtime type of b is B, so void B::MD(double) is called.

As long as you understand the principles and remember that overloading depends on the static type while overriding depends on the dynamic type, you can accurately determine exactly which function is called.