C++ Inheritance
Learn C++ Inheritance step by step with clear examples and exercises.
Title: Mastering C++ Inheritance: A full guide for Practical Depth
Why This Matters
In C++, inheritance is a powerful mechanism that allows you to create new classes based on existing ones, reusing and extending their functionality. Understanding inheritance can help you write more efficient, modular, and maintainable code in your projects. It's essential for object-oriented programming (OOP) and is often used in real-world applications, interviews, and exams to test your understanding of the language.
Prerequisites
Before diving into C++ inheritance, you should have a solid grasp of:
- Basic C++ syntax: variables, functions, loops, and control structures.
- Object-oriented programming concepts: classes, objects, encapsulation, and polymorphism.
- Access specifiers (public, private, protected) in C++.
- The concept of base classes and derived classes.
- Understanding the difference between inheritance, composition, and aggregation.
Core Concept
Base Classes and Derived Classes
Inheritance allows you to create a new class (derived class) that inherits properties and methods from an existing class (base class). The derived class is said to be a subclass of the base class, and it can extend, modify, or override the base class's functionality.
To inherit from a base class, you use the : symbol followed by the name of the base class in your derived class declaration:
class DerivedClass : public BaseClass {
// Derived class members and methods
};
In this example, DerivedClass is the derived class, and BaseClass is the base class. The public keyword specifies that the access level for inherited members from the base class will be public in the derived class.
Inheritance Types
There are three types of inheritance in C++:
- Public Inheritance: By default, when you inherit from a base class, it results in public inheritance. All members and functions of the base class are accessible in the derived class.
- Protected Inheritance: When you declare the base class with the
protectedaccess specifier, all members and functions of the base class become protected in the derived class. Protected members can be accessed within the derived class and its derived classes but not from outside.
- Private Inheritance: If you declare the base class with the
privateaccess specifier, all members and functions of the base class become private in the derived class. Private members can only be accessed within the derived class itself.
Constructors and Destructors in Inheritance
When inheriting from a base class, you should consider how constructors and destructors behave:
- Default constructor: If the base class has a default constructor, the derived class will also have a default constructor that initializes both the base and derived class members.
- Parameterized constructor: If the base class has a parameterized constructor, the derived class should also have one to ensure proper initialization of all members.
- Destructors: The destructor for the derived class is called before the base class's destructor, so you must ensure that any resources allocated in the base class are properly deallocated in the derived class's destructor.
Overriding and Hidden Base Class Members
In some cases, you may want to modify or replace a member function from the base class in the derived class. This is called overriding. To override a member function, you should declare it with the same signature (name, return type, and parameter list) as the base class function in the derived class:
class BaseClass {
public:
void printName() {
std::cout << "Base name\n";
}
};
class DerivedClass : public BaseClass {
public:
void printName() {
std::cout << "Derived name\n";
}
};
In this example, the printName() function in the derived class overrides the same function in the base class. When you call printName() on a DerivedClass object, it will print "Derived name" instead of "Base name".
If you declare a member with the same name and signature as a member from the base class, it is said to be hidden. Hidden members can still be accessed using the scope resolution operator (::) or by qualifying the member with the base class object.
Worked Example
In this example, we will create a simple hierarchy of classes using inheritance:
#include <iostream>
using namespace std;
class Animal {
public:
string name;
void setName(string n) {
name = n;
}
virtual void speak() {
cout << "Animal makes a sound.\n";
}
};
class Dog : public Animal {
public:
void speak() {
cout << "Dog barks.\n";
}
};
class Cat : public Animal {
public:
void speak() {
cout << "Cat meows.\n";
}
};
int main() {
Dog dog;
Cat cat;
dog.setName("Rex");
cat.setName("Whiskers");
cout << "Dog: ";
dog.speak();
cout << "\nCat: ";
cat.speak();
return 0;
}
In this example, we have a base class Animal with a name member and a virtual speak() function. We then create two derived classes, Dog and Cat, that inherit from the Animal class. Both derived classes override the speak() function to provide their specific sounds. In the main() function, we create instances of both derived classes, set their names, and call the speak() function for each one.
Common Mistakes
- Forgetting to use
virtual: If you don't make a member function virtual in the base class, it cannot be overridden in derived classes. This can lead to issues when calling the function polymorphically on objects of derived classes. - Ignoring access specifiers: When inheriting from a base class with private or protected members, you must ensure that the derived class has appropriate member functions or accessors to interact with those members.
- Not properly initializing base class members: If the base class has resources that need to be allocated and deallocated, make sure that your constructor and destructor handle them correctly in both the base and derived classes.
- Using hidden members incorrectly: Be aware of hidden members when overriding functions or creating new members with the same name as those in the base class. Use the scope resolution operator or qualifying the member with the base class object to access hidden members if necessary.
- Not understanding polymorphism: Polymorphism allows objects of different classes to be treated as if they were of a common parent class. Make sure you understand how it works and how to use virtual functions to achieve polymorphic behavior.
Practice Questions
- Create a base class
Shapewith members for the area and perimeter, and a virtual functioncalculateArea(). Create derived classesCircle,Rectangle, andTrianglethat inherit fromShapeand implement their respective area calculations. - Given the following code:
class Base {
public:
int x = 10;
};
class Derived : public Base {
public:
void printX() {
cout << x << "\n";
}
};
int main() {
Derived d;
d.printX();
}
What will be printed when running the code? Explain why.
- Create a class hierarchy for a simple bank account system with classes
Account,SavingsAccount, andCheckingAccount. Implement inheritance, overriding functions, and appropriate member variables to represent the differences between savings and checking accounts (e.g., interest rates, minimum balance).
FAQ
- What is the difference between public, protected, and private inheritance? Public inheritance allows derived classes to access all members of the base class. Protected inheritance restricts access to protected members in the base class. Private inheritance hides all members of the base class and can only be accessed within the derived class itself.
- Can a derived class have more members than its base class? Yes, a derived class can have additional members (variables or functions) that are not present in the base class.
- What is polymorphism, and how does it work with inheritance? Polymorphism allows objects of different classes to be treated as if they were of a common parent class. This is achieved through virtual functions in C++, which can have different implementations in derived classes based on the object's type at runtime.
- How are constructors and destructors handled in inheritance? When you create an object of a derived class, the constructor for both the base and derived classes is called. When the object goes out of scope, the destructor for the derived class is called first, followed by the base class's destructor.
- What happens when you override a member function in a derived class? Overriding a member function in a derived class creates a new implementation of that function with the same name and signature as the base class function. When you call the overridden function on an object of the derived class, it will execute the derived class's implementation instead of the base class's implementation.