C++继承基础:子类如何继承父类成员

在C++中,继承是面向对象编程的重要特性之一。它允许一个类(我们称之为子类派生类)通过“继承”另一个类(我们称之为父类基类)的成员(成员变量和成员函数),从而复用已有代码并扩展功能。想象一下,如果我们有一个“动物”类,里面包含“吃饭”“睡觉”这些通用行为,那么“狗”“猫”等具体动物类就可以直接继承“动物”类,无需重复编写这些通用代码。

一、如何定义父类和子类

首先,我们需要定义一个父类,再定义一个子类来继承它。以“动物”和“狗”为例:

// 父类:Animal(动物)
class Animal {
public:
    string name;  // 动物的名字(公开成员,子类可直接访问)
    int age;      // 动物的年龄(公开成员,子类可直接访问)

    // 吃饭的方法(公开成员函数,子类可直接调用)
    void eat() {
        cout << name << " is eating." << endl;
    }

    // 睡觉的方法(公开成员函数,子类可直接调用)
    void sleep() {
        cout << name << " is sleeping." << endl;
    }

private:
    int weight;  // 体重(私有成员,子类不可直接访问)
public:
    // 通过公开接口设置体重(间接访问私有成员)
    void setWeight(int w) {
        weight = w;
    }
    int getWeight() {
        return weight;
    }
};

// 子类:Dog(狗),继承自Animal
class Dog : public Animal {  // public继承:父类成员在子类中的访问权限保持原样
public:
    // 狗特有的方法(子类新增的成员)
    void bark() {
        cout << name << " is barking!" << endl;
    }
};

二、子类如何继承父类的成员变量

父类的成员变量分为 公开(public)保护(protected)私有(private) 三种,它们在子类中的访问规则不同:

  1. public 成员变量:子类可以直接访问。
    例如:Dog 类继承了 Animalnameage,所以在 Dog 中可以直接使用 nameage
   Dog myDog;
   myDog.name = "Buddy";  // 直接访问父类的public成员
   myDog.age = 3;         // 直接访问父类的public成员
  1. private 成员变量:子类不能直接访问,但可以通过父类提供的公开接口(如 setWeight/getWeight)间接操作。
    例如:Animalweight 是 private,子类 Dog 不能直接写 myDog.weight,但可以调用 myDog.setWeight(15) 来设置体重。

  2. protected 成员变量:仅子类和子类的子类可以直接访问(初学者暂时不用深入,了解即可)。

三、子类如何继承父类的成员函数

与成员变量类似,父类的成员函数也分不同权限,子类的访问规则如下:

  1. public 成员函数:子类可以直接调用。
    例如:Animaleat()sleep() 是 public,子类 Dog 可以直接调用 myDog.eat()myDog.sleep()

  2. private 成员函数:子类不能直接调用,需通过父类的公开接口间接调用(同私有变量逻辑)。

  3. protected 成员函数:仅子类可以直接调用(初学者了解即可)。

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

    myDog.eat();    // 调用父类的public成员函数(正常输出)
    myDog.bark();   // 调用子类新增的成员函数(输出 "Buddy is barking!")
    myDog.sleep();  // 调用父类的public成员函数(正常输出)

    myDog.setWeight(15);  // 通过父类的接口设置private成员
    cout << "Weight: " << myDog.getWeight() << endl;  // 输出 15
    return 0;
}

四、不同继承方式的影响

C++ 有三种继承方式,会影响子类对父类成员的访问权限:

继承方式 父类 public 成员在子类中的权限 父类 protected 成员在子类中的权限 父类 private 成员在子类中的权限
public public protected 不可见(无法直接访问)
private private private 不可见
protected protected protected 不可见

初学者注意
- 最常用的是 public 继承(默认不写时也可视为 public,但建议显式写出),此时父类的 public 和 protected 成员在子类中保持原权限,private 成员不可见。
- 其他两种方式(private/protected 继承)通常用于特殊场景(如隐藏父类接口),初学者可暂时忽略。

五、构造函数的继承与初始化

父类的构造函数不能被直接继承,但子类构造函数必须先初始化父类部分。例如,Animal 有一个构造函数:

class Animal {
public:
    string name;
    int age;
    Animal(string n, int a) : name(n), age(a) {  // 父类构造函数
        cout << "Animal " << name << " created." << endl;
    }
};

子类 Dog 构造时,需通过初始化列表调用父类构造函数:

class Dog : public Animal {
public:
    // 子类构造函数:先调用父类构造函数,再初始化自己
    Dog(string n, int a) : Animal(n, a) {  // 必须先调用父类构造函数
        cout << "Dog " << n << " created." << endl;
    }
};

关键点:子类对象创建时,会先执行父类构造函数,再执行子类构造函数。

六、总结

继承的核心是 代码复用扩展
- 子类可以直接继承父类的 public/protected 成员(变量/函数),避免重复编写通用逻辑。
- 父类的 private 成员需通过公开接口间接访问,保证数据封装性。
- 构造函数需显式调用父类构造函数,通过初始化列表完成父类部分的初始化。

通过继承,我们可以用更少的代码实现更复杂的功能,比如“猫”“鸟”等类都可以继承“动物”类,只需添加各自特有的方法(如 CatcatchMouse())。

小练习:尝试定义一个 Cat 类继承 Animal,并添加 catchMouse() 方法,然后创建 Cat 对象测试继承效果。

Xiaoye