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

相关推荐
bzmK1DTbd1 小时前
Git版本控制:Java项目中的分支管理与合并策略
java·开发语言·git
许长安1 小时前
RPC 同步调用基本使用方法:基于官方 RouteGuide 示例
c++·经验分享·笔记·rpc
Rust研习社1 小时前
为什么 Rust 没有空指针?
开发语言·后端·rust
kyriewen111 小时前
WebAssembly:前端界的“外挂”,让C++代码在浏览器里跑起来
开发语言·前端·javascript·c++·单元测试·ecmascript
其实防守也摸鱼3 小时前
CTF密码学综合教学指南--第九章
开发语言·网络·python·安全·网络安全·密码学·ctf
砚底藏山河3 小时前
Python量化开发:2026最佳实时股票数据API接口推荐与对比
开发语言·windows·python
AlunYegeer4 小时前
JAVA,以后端的视角理解前端。在全栈的路上迈出第一步。
java·开发语言·前端
浅念-5 小时前
刷穿LeetCode:BFS 解决 Flood Fill 算法
数据结构·c++·算法·leetcode·职场和发展·bfs·宽度优先
hixiong1235 小时前
C# OpenvinoSharp使用DINOv2模型进行图像相似度计算
开发语言·c#
DFT计算杂谈5 小时前
自动化脚本一键绘制三元化合物相图
java·运维·服务器·开发语言·前端·python·自动化