C++ 继承与派生类
讲解一下 C++ 中的继承(Inheritance)和派生类(Derived Class)。这是面向对象编程(OOP)的核心概念之一,用于实现代码复用和建立类之间的层次关系。
1. 核心概念
基类 (Base Class): 也被称为父类 (Parent Class)。它是被继承的类,定义了公共的属性和方法。
派生类 (Derived Class): 也被称为子类 (Child Class)。它从基类继承而来,继承了基类的特征,同时可以添加自己新的成员,或修改继承来的成员。
核心思想: “是一个 (is-a)” 关系。例如,如果一个类 Dog
继承自类 Animal
,那么我们就可以说 “Dog is an Animal”(狗是一个动物)。这种关系是继承是否适用的重要判断依据。
2. 继承的基本语法
cpp
class DerivedClass : access-specifier BaseClass {// 派生类的新成员(属性和方法) };
DerivedClass
: 派生类的名称。access-specifier
: 访问说明符,可以是public
,protected
, 或private
。它决定了从基类继承来的成员在派生类中的最高访问权限(后面会详细解释)。最常用的是public
继承。BaseClass
: 基类的名称。
示例:一个简单的继承
cpp
#include <iostream> #include <string> using namespace std;// 基类 class Animal { public:void eat() {cout << name << " is eating." << endl;}string name; };// 派生类 Dog 以 public 方式继承 Animal class Dog : public Animal { public:void bark() {// 可以访问基类的 public 成员 namecout << name << " says: Woof! Woof!" << endl; } };// 派生类 Cat 以 public 方式继承 Animal class Cat : public Animal { public:void meow() {cout << name << " says: Meow! Meow!" << endl;} };int main() {Dog myDog;myDog.name = "Buddy"; // 继承自 Animal 的成员myDog.eat(); // 继承自 Animal 的方法myDog.bark(); // 派生类自己的方法Cat myCat;myCat.name = "Whiskers";myCat.eat();myCat.meow();return 0; }
输出:
text
Buddy is eating. Buddy says: Woof! Woof! Whiskers is eating. Whiskers says: Meow! Meow!
3. 继承中的访问控制
访问说明符在继承中起到了关键作用,它决定了基类成员在派生类中的可见性。
基类中的访问权限 | 继承方式 | 在派生类中的访问权限 |
---|---|---|
public | public | public |
protected | public | protected |
private | public | 不可访问 (Inaccessible) |
public | protected | protected |
protected | protected | protected |
private | protected | 不可访问 (Inaccessible) |
public | private | private |
protected | private | private |
private | private | 不可访问 (Inaccessible) |
重要规则总结:
无论何种继承方式,基类的
private
成员在派生类中都是不可直接访问的。 如果需要在派生类中访问,应将这些成员在基类中声明为protected
。继承方式决定了基类成员在派生类中的“最高访问权限”。
public
继承: 保持基类成员的原有访问权限。这是最常用的方式,用于建立 “is-a” 关系。protected
继承: 将基类的public
和protected
成员都变为派生类的protected
成员。很少使用。private
继承: 将基类的所有可访问成员(public
,protected
) 都变为派生类的private
成员。这表示 “根据实现继承” 而非 “根据接口继承”,通常可以用组合(在一个类中包含另一个类的对象) 来替代,组合更受青睐。
示例:protected 成员
cpp
class Animal { protected: // protected 成员int age; // 派生类可以访问,但类外部不能访问public:string name; };class Dog : public Animal { public:void setAge(int a) {age = a; // 正确:age 在派生类中是 protected,可以访问} };int main() {Dog myDog;// myDog.age = 5; // 错误:age 是 protected,在类外部不可访问myDog.name = "Buddy"; // 正确:name 是 publicmyDog.setAge(5); // 通过公共方法访问 protected 成员return 0; }
4. 构造函数和析构函数的调用顺序
当创建派生类对象时,构造函数的调用顺序非常重要:
基类的构造函数
派生类中成员对象的构造函数(按声明顺序)
派生类的构造函数
析构函数的调用顺序完全相反:
派生类的析构函数
派生类中成员对象的析构函数(按声明逆序)
基类的析构函数
示例:
cpp
#include <iostream> using namespace std;class Base { public:Base() { cout << "Base constructor" << endl; }~Base() { cout << "Base destructor" << endl; } };class Member { public:Member() { cout << "Member constructor" << endl; }~Member() { cout << "Member destructor" << endl; } };class Derived : public Base { public:Derived() { cout << "Derived constructor" << endl; }~Derived() { cout << "Derived destructor" << endl; } private:Member mem; // 成员对象 };int main() {Derived obj;return 0; }
输出:
text
Base constructor Member constructor Derived constructor Derived destructor Member destructor Base destructor
5. 向基类构造函数传递参数
派生类无法直接初始化从基类继承来的成员。必须通过调用基类的构造函数来完成。
方法是在派生类构造函数的初始化列表中调用基类的构造函数。
cpp
class Base { public:Base(int value) : baseValue(value) { // 基类构造函数需要参数cout << "Base constructed with value: " << baseValue << endl;}int baseValue; };class Derived : public Base { public:// 通过初始化列表将参数 100 传递给基类构造函数Derived() : Base(100) { cout << "Derived constructor" << endl;}// 派生类构造函数也可以接收参数,并传递给基类Derived(int x, int y) : Base(x), derivedValue(y) { cout << "Derived constructed with value: " << derivedValue << endl;} private:int derivedValue; };int main() {Derived d1; // 输出: Base constructed with value: 100Derived d2(5, 10); // 输出: Base constructed with value: 5return 0; }
总结
特性 | 描述 |
---|---|
目的 | 代码复用,建立类层次结构(is-a 关系) |
语法 | class Derived : public Base { ... }; |
访问控制 | public 继承最常用。private 成员永远不可在派生类中直接访问。 |
构造顺序 | 基类 -> 派生类成员 -> 派生类 |
析构顺序 | 派生类 -> 派生类成员 -> 基类 |
传递参数 | 在派生类构造函数的初始化列表中调用基类构造函数:Derived() : Base(arg) |