Basics of C++ Inheritance: How Subclasses Inherit Members from Parent Classes

In C++, inheritance is one of the important features of object-oriented programming. It allows a class (called a subclass or derived class) to “inherit” members (member variables and member functions) from another class (called the base class or parent class), thereby reusing existing code and extending functionality. Imagine if we have an “Animal” class with general behaviors like “eating” and “sleeping”; then specific animal classes like “Dog” and “Cat” can directly inherit from the “Animal” class without repeating these general codes.

I. How to Define Parent and Child Classes

First, we need to define a parent class and then a child class that inherits from it. Taking “Animal” and “Dog” as examples:

// Parent class: Animal (动物)
class Animal {
public:
    string name;  // Animal's name (public member, accessible directly by subclasses)
    int age;      // Animal's age (public member, accessible directly by subclasses)

    // Method to eat (public member function, callable directly by subclasses)
    void eat() {
        cout << name << " is eating." << endl;
    }

    // Method to sleep (public member function, callable directly by subclasses)
    void sleep() {
        cout << name << " is sleeping." << endl;
    }

private:
    int weight;  // Weight (private member, not directly accessible by subclasses)
public:
    // Public interface to set weight (indirect access to private member)
    void setWeight(int w) {
        weight = w;
    }
    int getWeight() {
        return weight;
    }
};

// Child class: Dog (狗), inherits from Animal
class Dog : public Animal {  // public inheritance: Parent class members retain their original access rights in the subclass
public:
    // Dog-specific method (new member in subclass)
    void bark() {
        cout << name << " is barking!" << endl;
    }
};

II. How a Subclass Inherits Parent Class Member Variables

Parent class member variables are categorized into three access levels: public, protected, and private. Their access rules in the subclass differ:

  1. public member variables: Directly accessible by the subclass.
    For example, the Dog class inherits name and age from Animal, so name and age can be directly used in Dog.
   Dog myDog;
   myDog.name = "Buddy";  // Directly access parent class's public member
   myDog.age = 3;         // Directly access parent class's public member
  1. private member variables: Not directly accessible by the subclass, but can be indirectly manipulated via public interfaces provided by the parent class (e.g., setWeight/getWeight).
    For example, Animal’s weight is private. The subclass Dog cannot directly write myDog.weight, but can call myDog.setWeight(15) to set the weight.

  2. protected member variables: Only directly accessible by the subclass and its subclasses (beginners can learn about this briefly).

III. How a Subclass Inherits Parent Class Member Functions

Similar to member variables, parent class member functions have different access rules based on their permissions:

  1. public member functions: Directly callable by the subclass.
    For example, Animal’s eat() and sleep() are public, so the subclass Dog can directly call myDog.eat() or myDog.sleep().

  2. private member functions: Not directly callable by the subclass; must be indirectly called via the parent class’s public interface (same logic as private variables).

  3. protected member functions: Only directly callable by the subclass (beginners can learn about this briefly).

int main() {
    Dog myDog;
    myDog.name = "Buddy";
    myDog.age = 3;

    myDog.eat();    // Call parent class's public member function (outputs "Buddy is eating.")
    myDog.bark();   // Call subclass's new member function (outputs "Buddy is barking!")
    myDog.sleep();  // Call parent class's public member function (outputs "Buddy is sleeping.")

    myDog.setWeight(15);  // Set private member via parent class interface
    cout << "Weight: " << myDog.getWeight() << endl;  // Outputs 15
    return 0;
}

IV. Impact of Different Inheritance Methods

C++ has three inheritance methods that affect the access rights of the parent class members in the subclass:

Inheritance Method Access Right of Parent’s public members in Subclass Access Right of Parent’s protected members in Subclass Access Right of Parent’s private members in Subclass
public public protected Invisible (cannot be directly accessed)
private private private Invisible
protected protected protected Invisible

Note for Beginners:
- The most commonly used is public inheritance (can be implicitly used if not specified, but explicit writing is recommended). In this case, the parent class’s public and protected members retain their original permissions, while private members are invisible.
- The other two methods (private/protected inheritance) are typically used for special scenarios (e.g., hiding parent class interfaces) and can be temporarily ignored by beginners.

V. Inheritance and Initialization of Constructors

The parent class constructor cannot be directly inherited, but the subclass constructor must first initialize the parent class part. For example, if Animal has a constructor:

class Animal {
public:
    string name;
    int age;
    Animal(string n, int a) : name(n), age(a) {  // Parent class constructor
        cout << "Animal " << name << " created." << endl;
    }
};

When constructing the subclass Dog, the initialization list must be used to call the parent class constructor:

class Dog : public Animal {
public:
    // Subclass constructor: First call parent class constructor, then initialize self
    Dog(string n, int a) : Animal(n, a) {  // Must call parent class constructor first
        cout << "Dog " << n << " created." << endl;
    }
};

Key Point: When a subclass object is created, the parent class constructor is executed first, followed by the subclass constructor.

VI. Summary

The core of inheritance is code reuse and extension:
- Subclasses can directly inherit the parent class’s public/protected members (variables/functions) to avoid repetitive writing of general logic.
- The parent class’s private members must be accessed indirectly through public interfaces to ensure data encapsulation.
- Constructors must explicitly call the parent class constructor via the initialization list to complete initialization of the parent class part.

Through inheritance, more complex functions can be implemented with less code (e.g., the “Cat” and “Bird” classes can all inherit from “Animal” and add their own specific methods like Cat’s catchMouse()).

Practice Exercise: Try defining a Cat class that inherits from Animal and add a catchMouse() method. Then create a Cat object to test the inheritance effect.

Xiaoye