C++ 继承

一、继承是什么?为什么要用继承?

继承是面向对象代码复用的核心手段。

  • 允许在保留原有类的基础上扩展功能,产生新类。
  • 原有类叫 基类/父类 ,新类叫 派生类/子类
  • 本质:类设计层面的复用,避免重复写相同成员。

没继承之前(代码冗余)

Student 和 Teacher 都有姓名、地址、电话、age、identity(),重复定义很麻烦。

用继承之后(代码复用)

把公共成员抽到 Person 基类,Student/Teacher 继承它,直接复用。

cpp 复制代码
class Person {
protected:
    string _name = "张三";
    string _address;
    string _tel;
    int _age = 18;
public:
    void identity() { cout << "认证:" << _name << endl; }
};

// 子类 : 继承方式 父类
class Student : public Person {
protected:
    int _stuid;
public:
    void study() {}
};

class Teacher : public Person {
protected:
    string _title;
public:
    void teaching() {}
};

二、继承的定义格式

cpp 复制代码
class 派生类 : 继承方式 基类
{
    // 新增成员
};

示例:

cpp 复制代码
class Student : public Person
{
public:
    int _stuid;
};

三、三种继承方式(最重要表格)

基类成员在派生类中的访问权限 = 取更小的那个
public > protected > private

基类成员 public 继承 protected 继承 private 继承
public public protected private
protected protected protected private
private 不可见 不可见 不可见

关键结论

  1. 基类 private 成员无论怎么继承,在子类都不可见
  2. 想让子类能访问、但类外不能访问 → 用 protected
  3. 实际开发 99% 用 public 继承
  4. class 默认 private 继承;struct 默认 public 继承。

四、基类 ↔ 派生类对象转换(切片/切割)

只有 public 继承才支持

1. 合法(可以)

  • 派生类对象 → 赋值给 基类对象
  • 派生类对象 → 赋值给 基类指针
  • 派生类对象 → 赋值给 基类引用

原理:把派生类中"属于基类的那部分"切过来用。

cpp 复制代码
Student s;
Person p = s;    // 可以
Person* pp = &s; // 可以
Person& rp = s;  // 可以

2. 不合法(不行)

  • 基类对象不能赋值给派生类对象
cpp 复制代码
Person p;
Student s = p; // 报错

五、继承中的作用域:隐藏(超级高频考点)

隐藏规则

  1. 基类和子类有独立作用域
  2. 子类和基类 同名成员 → 子类屏蔽基类 → 叫 隐藏
  3. 成员函数只要函数名相同就构成隐藏,不管参数!
  4. 想访问基类同名成员:基类::成员

示例:同名变量隐藏

cpp 复制代码
class Person {
protected:
    int _num = 111; // 身份证
};

class Student : public Person {
protected:
    int _num = 999; // 学号 → 隐藏父类
public:
    void Print() {
        cout << _num << endl;         // 子类 999
        cout << Person::_num << endl; // 父类 111
    }
};

示例:函数名相同即隐藏(不是重载)

cpp 复制代码
class A {
public:
    void func() { cout << "func()\n"; }
};

class B : public A {
public:
    void func(int i) { cout << "func(int)\n"; } // 隐藏 A::func()
};

// 调用
B b;
b.func(10);  // 走子类
// b.func(); // 报错!被隐藏了
b.A::func(); // 必须加类域才能调

六、派生类的 4 大默认成员函数(必考)

派生类的构造、拷贝构造、赋值、析构,必须配合基类

1. 构造函数

  • 子类构造 必须先调用基类构造
  • 基类无默认构造 → 子类必须在初始化列表显式调用
cpp 复制代码
Student(const char* name, int num) 
    : Person(name)  // 先初始化父类
    , _num(num)
{}

2. 拷贝构造

  • 子类拷贝构造 → 必须调用 基类拷贝构造
cpp 复制代码
Student(const Student& s)
    : Person(s)
    , _num(s._num)
{}

3. 赋值重载 operator=

  • 子类赋值会隐藏 父类赋值,必须显式调用
cpp 复制代码
Student& operator=(const Student& s) {
    if (this != &s) {
        Person::operator=(s); // 显式调用父类赋值
        _num = s._num;
    }
    return *this;
}

4. 析构函数

  • 子类析构 自动调用父类析构
  • 调用顺序:先析构子类 → 再析构父类
  • 构造顺序:先构造父类 → 再构造子类

4.2 如何实现一个 不能被继承的类

两种方法:

方法 1:C++98(构造函数私有)

  • 子类必须调用父类构造,私有后子类无法调用,无法实例化。
cpp 复制代码
class NonInherit {
private:
    NonInherit() {} // 私有构造
};

方法 2:C++11 final 关键字(最简单)

cpp 复制代码
class Base final {
    // 此类不能被继承
};

相关推荐
殇淋狱陌2 小时前
【初始Python】Python学习基础(数据类型、定义、变量、下标、目前的开发语言对比)
开发语言·python·学习
lsx2024062 小时前
Ruby 迭代器
开发语言
史迪仔01122 小时前
[QML] Popup 与 Dialog
开发语言·前端·javascript·c++·qt
John.Lewis2 小时前
C++加餐课-stack_queue:计算器-逆波兰表达式
开发语言·c++
DeepModel2 小时前
通俗易懂讲透 Mini-Batch K-means
开发语言·人工智能·机器学习·kmeans·batch
happy_baymax2 小时前
基于正弦波直接移相的PSFB控制方法
开发语言
傻啦嘿哟2 小时前
如何用 Python 拆分 Word 文件:高效分割大型文档的完整指南
开发语言·c#
高斯林.神犇2 小时前
五、注解方式管理bean
java·开发语言
hoiii1872 小时前
C# 读取 CSV/Excel 文件数据至 DataGridView
开发语言·c#·excel