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 {
    // 此类不能被继承
};

相关推荐
懂点Tech3 小时前
ctfshow MISC入门图片篇(信息附加):misc41解题思路
开发语言·python
catchadmin3 小时前
PHP 8.7 协程新提案 让跨协程代码共享请求身份
开发语言·php
念何架构之路4 小时前
go-grpc设计思想
开发语言·后端·golang
酷炫码神5 小时前
Day 10|Pinia 状态管理:把散落在各组件的数据,收进一个全局仓库
android·开发语言·javascript
Elastic 中国社区官方博客5 小时前
我们如何通过 70 多个拉取请求,将 OpenTelemetry Java agent 迁移到 invokedynamic
java·大数据·开发语言·elasticsearch·搜索引擎·全文检索·es
tyn1886 小时前
记录本地安装nacos服务的方法及异常
java·开发语言
步行cgn6 小时前
Spring MVC 视图机制的核心接口与类
java·开发语言·后端·spring·mvc
传奇开心果编程7 小时前
【Rust入门练中学】第5课:引用与借用
开发语言·rust·引用·编程语言理论·通用编程语言·借用
qetfw7 小时前
Bash 变量:export、位置参数与默认值展开验证
linux·开发语言·chrome·bash
电子云与长程纠缠7 小时前
UE5魂系游戏开发教程05
开发语言·学习·游戏·ui·ue5