Unlocking the Power of Inheritance in Object-Oriented Programming
Inheritance is a fundamental concept in object-oriented programming (OOP) that enables the creation of derived classes from a base class. This powerful feature allows derived classes to inherit characteristics from their base class, making it possible to build complex systems with ease.
The Magic of Shadowing
But what happens when a derived class defines a function with the same name as one in its base class? This phenomenon is known as shadowing, where the derived class’s function takes precedence over the base class’s function. For instance, consider a scenario where we define a print()
function in both a Base
and a Derived
class.
class Base {
public:
void print() {
std::cout << "Base class" << std::endl;
}
};
class Derived : public Base {
public:
void print() {
std::cout << "Derived class" << std::endl;
}
};
When we call the print()
function using an object of the Derived
class, the print()
function of the Derived
class executes, effectively shadowing the print()
function of the Base
class.
Accessing Shadowed Functions
So, how do we access the shadowed function of the base class? The answer lies in using the scope resolution operator ::
. By employing this operator, we can explicitly call the shadowed function from the base class. Alternatively, we can use a pointer of the base class to point to an object of the derived class and then call the function from that pointer.
int main() {
Derived obj;
obj.print(); // Output: Derived class
obj.Base::print(); // Output: Base class
Base* ptr = &obj;
ptr->print(); // Output: Base class
return 0;
}
Real-World Examples
Let’s dive into some practical examples to illustrate these concepts:
Example 1: Shadowing in Action
In this example, we define a print()
function in both the Base
and Derived
classes. When we call the print()
function from a Derived
object, the print()
function of the Derived
class executes, shadowing the print()
function of the Base
class.
class Base {
public:
void print() {
std::cout << "Base class" << std::endl;
}
};
class Derived : public Base {
public:
void print() {
std::cout << "Derived class" << std::endl;
}
};
int main() {
Derived obj;
obj.print(); // Output: Derived class
return 0;
}
Example 2: Accessing Shadowed Functions
Here, we demonstrate how to access the shadowed print()
function of the Base
class using the scope resolution operator ::
.
int main() {
Derived obj;
obj.Base::print(); // Output: Base class
return 0;
}
Example 3: Calling Shadowed Functions from Derived Classes
In this example, we show how to call the shadowed print()
function from the Base
class inside the Derived
class itself.
class Derived : public Base {
public:
void print() {
std::cout << "Derived class" << std::endl;
Base::print(); // Output: Base class
}
};
int main() {
Derived obj;
obj.print();
return 0;
}
Example 4: Using Pointers to Access Shadowed Functions
In this final example, we create a pointer of Base
type that points to a Derived
object. When we call the print()
function using this pointer, it calls the print()
function of the Base
class.
int main() {
Derived obj;
Base* ptr = &obj;
ptr->print(); // Output: Base class
return 0;
}
The Power of Virtual Functions
To override the base function instead of accessing it, we need to employ virtual functions in the base class. By doing so, we can ensure that the correct function is called based on the object’s type, rather than its declared type.
class Base {
public:
virtual void print() {
std::cout << "Base class" << std::endl;
}
};
class Derived : public Base {
public:
void print() override {
std::cout << "Derived class" << std::endl;
}
};
int main() {
Base* ptr = new Derived();
ptr->print(); // Output: Derived class
return 0;
}