CPP Inheritance and Derivation
Class Inheritance and Derivation
Overview
Inheritance means obtaining attributes and behavioral characteristics from ancestors.
Class inheritance is when a new class obtains existing characteristics from an existing class. From another perspective, the process of creating a new class from an existing class is called class derivation.
They are essentially the same, just the same process viewed from different angles.
Purpose of Inheritance and Derivation
Purpose of inheritance: Achieve reuse of design and code.
Purpose of derivation: When new problems arise and the original program cannot solve them well, the original program needs to be modified.
Different classification standards
Base classes that directly participate in deriving a class are called direct base classes, and base classes of base classes or even higher-level base classes are called indirect base classes.
A derived class can have multiple base classes at the same time, this situation is called multiple inheritance. Similarly, the case with only one base class is called single inheritance.
Definition of Derived Classes
Derived class definition syntax:
1 | class DerivedClassName: inheritance_mode BaseClassName1,...,inheritance_mode BaseClassNamen |
Process of Creating Derived Classes
- Absorb base class members
After absorbing base class members, the derived class actually contains all members from its base classes except constructors and destructors.
- Modify base class members
If a derived class declares a new member with the same name as a base class member, the derived new member hides or overrides the outer member with the same name.
- Add new members
Derived classes add new members to develop functionality.
Access Control
Access mainly comes from two aspects: first, new members in derived classes accessing members inherited from base classes; second, outside the derived class, accessing members inherited from base classes through derived class objects.
Inheritance modes are mainly divided into three types, and their respective characteristics are listed below.
Public Inheritance (public)
The access attributes of public and protected members of the base class remain unchanged in the derived class, but private members of the base class cannot be directly accessed.
Member functions in derived classes can directly access public and protected members of the base class, but cannot directly access private members of the base class.
When accessing members inherited from the base class through derived class objects, only public members can be accessed.
Protected Inheritance (protected)
Both public and protected members of the base class appear in the derived class as protected, but private members of the base class cannot be directly accessed.
Member functions in derived classes can directly access public and protected members of the base class, but cannot directly access private members of the base class.
Through derived class objects, no members inherited from the base class can be directly accessed.
Private Inheritance (private)
Both public and protected members of the base class appear in the derived class as private, but private members of the base class cannot be directly accessed.
Member functions in derived classes can directly access public and protected members of the base class, but cannot directly access private members of the base class.
Through derived class objects, no members inherited from the base class can be directly accessed.
It is not difficult to see that the second point of these three inheritance methods is exactly the same, which follows the principle of data sharing and protection. Derived class objects can access public members under public inheritance, but cannot access them in other cases for data protection.
Attached is a good detailed explanation
Type Compatibility Rules
Type compatibility rules mean that anywhere a base class object is needed, public derived class objects can be used as substitutes. Through public inheritance, derived classes get all members of the base class except constructors and destructors, possessing all functionality of the base class. (Protected and private inheritance don’t work because objects cannot access any members inherited from the base class)
Example:
1 | class B{...} |
Based on the above code, there are three substitution situations:
Derived class objects can be implicitly converted to base class objects
b1=d1;Derived class objects can initialize base class references
B &rb=d1;Derived class pointers can be implicitly converted to base class pointers
pb1=&d1
This compatibility rule allows us to use the same function to uniformly handle base class and public derived class objects. That is, when the formal parameter is a base class object (reference, pointer), the actual parameter can be a derived class object or pointer. This greatly improves program efficiency.
Constructors and Destructors of Derived Classes
Constructors of derived classes are only responsible for initializing newly added members of the derived class. For all members inherited from the base class, initialization is still done by the base class constructor. Finally, cleanup of derived objects also requires adding new destructors.
Constructors
Since derived classes cannot access many data members in the base class, they need to rely on the base class constructor. When constructing derived class objects, the base class constructor is called first, then the newly added member objects of the derived class are initialized.
General syntax form:
1 | DerivedClassName::DerivedClassName(parameter_list):BaseClassName1(BaseClassName1_initialization_parameters),...,BaseClassNamen(BaseClassNamen_initialization_parameters) |
General order of constructor execution:
- Call base class constructors in the order they were declared during inheritance (left to right).
- Initialize newly added member objects of the derived class in the order they were declared in the class.
- Execute the content in the derived class constructor body.
Copy Constructor
Derived classes also use the base class copy constructor when performing copy construction.
Example: If writing a copy constructor for the Derived class (with Base class as base class), the form is:
1 | Derived::Derived(const Derived &v): Base(v){...} |
Here the base class uses a reference to the derived class, which fully complies with the type compatibility rules where derived class objects can be used to initialize the base class.
Destructor
Actually, it’s completely consistent with the idea of constructors, the biggest difference is that the order of destruction is completely opposite to the order of initialization.
Identification and Access of Derived Class Members
Scope Resolution Operator
“::” is the scope resolution operator, used to specify the name of the class where the member to be accessed is located.
If a derived class declares a new function with the same name as a base class member function, even if the function parameter lists are different, all overloaded forms of the inherited function with the same name from the base class will be hidden.
The same applies to data members. New members with the same name will override the base class; if multiple inheritance base classes are duplicated, ambiguity will arise and must be resolved by using class names and scope resolution operators to identify members.
Only functions defined in the same scope are called overloads.
The using keyword can be used to use identifiers from other scopes.
Virtual Base Classes
Suppose a derived class inherits from multiple base classes, and some or all of these base classes are derived from another common base class, then in this derived class, there will be the same names and multiple copies in memory, causing program overhead.
At this time, the common base class can be set as a virtual base class, so that data members with the same name inherited from different paths have only one copy in memory, and the same function name has only one mapping.
Syntax form:
1 | class DerivedClassName:virtual inheritance_mode BaseClassName |
Constructors of Virtual Base Classes and Their Derived Classes
In the entire inheritance relationship, all derived classes that directly or indirectly inherit from virtual base classes must list initialization of virtual base classes in the member initialization list of constructors.
When calling the constructor of virtual classes, the C++ compiler will specify the constructor of the most derived class to call the constructor of the virtual base class, so there’s no need to worry about multiple repeated calls.
The general order of constructing an object of a class is:
If the class has direct or indirect virtual base classes, then the constructor of the virtual base class is executed first.
If there are other base classes, initialize them in the order they appear in the inheritance declaration list, but during construction, their virtual base class constructors are no longer executed.
Initialize newly added member objects in the order they appear in the definition. For class-type member objects, if they appear in the constructor initialization list, execute the constructor with the specified parameters; if not, execute the default constructor; for basic-type member objects, if they appear in the initialization list, use the specified value to assign initial value, otherwise do nothing.
Execute the constructor function body.









