当前位置: 首页 > ds >正文

CPP继承

继承

一、继承概述

1、为什么需要继承

如下示例,Person 类、Student 类、Teacher 类有大量重复的代码,造成代码冗余,降低开发效率。

在这里插入图片描述

我们可以通过继承来解决这一问题。在面向对象的编程语言中,继承是一个核心概念。主要作用将重复的代码统一定义在父类中,子类从父类继承,同时继承也是实现多态的重要条件。

2、什么是继承

继承就是一个新类从现有类派生的过程。新类称之为派生类或子类,原有的类称之为基类或父类;子类可以继承父类中的成员,从而可以提高代码的可重用性。

在这里插入图片描述
继承关系下,子类和父类存在 is a 的 关系。例如,狗是动物,猫是动物,老虎是一个动物等等。那么可以说动物类是一个父类,老虎、猫、狗都是动物类的子类。

在这里插入图片描述
在继承关系下父类更通用,子类更具体。也就是说父类拥有子类的共同特性,子类可以具备独有的特性。
在这里插入图片描述

二、继承的实现

C++ 中类实现继承的形式如下:

class 派生类名:[继承方式]基类名 //默认是private继承方式
{
}

继承方式有 3 种类型,分别为共有型 (public),保护型 (protected) 和私有型 (private);: 表示基类和派生类之间的继承关系的符号。

示例:

Person 类

Person 类作为父类,其包含了 public 修饰的属性:

#pragma once
#include <string>
class Person
{
public:
std::string name;
int age;
};

Student 类

继承了 Person 类,子类从父类继承 public 成员

Student.h

#pragma once
#include "Person.h"
class Student: public Person
public:
void show();
};

Student.cpp

#include "Student.h"
#include <iostream>
using namespace std;
void Student::show()
{
cout << "name:" << name << endl;
cout << "age:" << age << endl;
}

Main.cpp

#include <iostream>
#include "Student.h"
int main()
Student s;
s.name = "张三"; //从父类继承的成员
s.age = 20;
s.show();
}

三、派生类的访问控制

在 C++ 中,类成员的访问权限分为 public (公共)、protected (受保护) 或 private (私有) 3 种。其中父类的 public 和 protected 成员允许子类继承,private 成员不能被继承。

以 public 继承模式为例,访问控制权限如下:

访问publicprotectedprivate
同一个类yesyesyes
派生类yesyesno
外部的类yesnono

类 A 的定义:

#pragma once
class A
{
public:
int num_public; //公有的成员任何类都可以访问
protected:
int num_protected; //受保护的成员可以在当前类和子类中访问
private:
int num_private; //私有的成员只能在当前类中访问
};

类 B 继承于类 A:

#pragma once
#include "A.h"
class B: public A
public:
B();
B(int a, int b);
void print();
};

类 B 中访问父类中的成员:

#include "B.h"
#include <iostream>
using namespace std;
B::B() {}
B::B(int a, int b)
{
this->num_public = a;
this->num_protected = b;
}
void B::print()
{
cout << "public:" << num_public << endl;
cout << "protected:" << num_protected << endl;
//cout << "private:" << num_private << endl; //编译错误,私有成员,不能被子类继承
}

四、继承类型

C++ 支持三种继承类型,分别是 public、protected 及 private 类型,这些继承类型影响着基类成员在派生类中的访问权限。

1、访问权限变化总览
基类成员权限            public继承         protected继承         private继承
---------------------------------------------------------------------------
public成员    ───►   public(外部可访问)  protected(外部不可)   private(外部不可)
protected成员 ───►   protected(外部不可) protected(外部不可)   private(外部不可)
private成员   ───►   不可访问               不可访问                不可访问
  • 继承方式只会影响基类 public / protected 成员在派生类中的可见性,不会影响派生类对自己新成员的访问控制。
  • private 成员无论哪种继承方式,子类都不能直接访问。

直观理解:

  • public继承:原汁原味 —— public 还是 public,protected 还是 protected。
  • protected继承:降一级 —— public 变 protected,protected 不变。
  • private继承:全收进屋 —— public 和 protected 全变 private。
2、基类定义
#pragma once
#include <iostream>
using namespace std;class Base
{
public:void func_public() { cout << "Base::func_public()" << endl; }protected:void func_protected() { cout << "Base::func_protected()" << endl; }private:void func_private() { cout << "Base::func_private()" << endl; }
};
3、公有继承 (Public Inheritance)
规则
  • 基类 public 成员 → 派生类 public
  • 基类 protected 成员 → 派生类 protected
  • 外部依然可以访问继承的 public 成员
代码示例

SubPublic.h

#pragma once
#include "Base.h"
class SubPublic : public Base
{
public:void func();
};

SubPublic.cpp

#include "SubPublic.h"
#include <iostream>
using namespace std;void SubPublic::func()
{cout << "[public继承] 子类内部可以访问父类 public + protected 成员" << endl;func_public();    // ✅func_protected(); // ✅
}

测试

SubPublic pub;
pub.func();
pub.func_public(); // ✅ 外部可访问
4、私有继承 (Private Inheritance)
规则
  • 基类 public 成员 → 派生类 private
  • 基类 protected 成员 → 派生类 private
  • 外部无法访问这些继承的成员
代码示例

SubPrivate.h

#pragma once
#include "Base.h"class SubPrivate : private Base
{
public:void func();
};

SubPrivate.cpp

#include "SubPrivate.h"
#include <iostream>
using namespace std;void SubPrivate::func()
{cout << "[private继承] 子类内部可以访问父类 public + protected 成员" << endl;func_public();    // ✅func_protected(); // ✅
}

测试

SubPrivate pri;
pri.func();
// pri.func_public(); // ❌ 外部不可访问
5、保护继承 (Protected Inheritance)
规则
  • 基类 public 成员 → 派生类 protected
  • 基类 protected 成员 → 派生类 protected
  • 外部无法直接访问,但派生类的子类可以访问
代码示例

SubProtected.h

pragma once
#include "Base.h"class SubProtected : protected Base
{
public:void func();
};

SubProtected.cpp

#include "SubProtected.h"
#include <iostream>
using namespace std;void SubProtected::func()
{cout << "[protected继承] 子类内部可以访问父类 public + protected 成员" << endl;func_public();    // ✅func_protected(); // ✅
}

6、保护继承的子类

Subclass.h

#pragma once
#include "SubProtected.h"class Subclass : public SubProtected
{
public:void test();
};

Subclass.cpp

#include "Subclass.h"
#include <iostream>
using namespace std;void Subclass::test()
{cout << "[保护继承的子类] 仍然可以访问父类的 public + protected 成员" << endl;func_public();    // ✅func_protected(); // ✅
}

测试

Subclass subc;
subc.test();
// subc.func_public(); // ❌ 外部不可访问

五、继承中的构造函数与析构函数

基类中的构造函数、析构函数和拷贝构造函数不能被派生类继承。

1、构造函数和析构函数的执行顺序

继承关系下:

  • 当派生类对象被创建时,先调用基类的构造函数,然后再调用派生类的构造函数。
  • 析构时顺序相反,先调用派生类的析构函数,再调用基类的析构函数。
  • 基类的构造函数、析构函数以及拷贝构造函数不会被继承到派生类。
#include <iostream>
using namespace std;class A
{
public:A(){cout << "A类构造函数" << endl;}~A(){cout << "A类析构函数" << endl;}
};class B : public A
{
public:B(){cout << "B类构造函数" << endl;}~B(){cout << "B类析构函数" << endl;}
};class C : public B
{
public:C(){cout << "C类构造函数" << endl;}~C(){cout << "C类析构函数" << endl;}
};int main()
{C c;  // 创建C类对象return 0;
}

程序输出

A类构造函数
B类构造函数
C类构造函数C类析构函数   
B类析构函数
A类析构函数
  • 当你定义(创建)一个对象时,系统会自动调用该对象所属类的构造函数,用来完成对象的初始化。
  • 当对象的生命周期结束时,系统会自动调用对应类的析构函数,用来完成清理工作(比如释放内存、关闭文件等)。
2、子类中调用父类构造
  • 当基类只提供带参数的构造函数且没有无参构造函数时,派生类必须在其初始化列表中显式调用基类的有参构造函数,否则编译会报错。
  • 如果基类有无参构造函数,则派生类会默认调用基类的无参构造函数。

基类 Person 示例

Person.h

#pragma once
#include <string>class Person
{
public:Person(std::string name);std::string getName();private:std::string name;
};

Person.cpp

#include "Person.h"Person::Person(std::string name) : name(name)
{
}std::string Person::getName()
{return name;
}

派生类 Student 示例

Student.h

#pragma once
#include "Person.h"class Student : public Person
{
public:Student();Student(std::string name);
};

Student.cpp

#include "Student.h"// 当基类无默认构造时,派生类必须显示调用基类有参构造函数
Student::Student() : Person("")
{
}Student::Student(std::string name) : Person(name)
{
}

测试 main.cpp

#include <iostream>
#include "Student.h"
using namespace std;int main()
{Student s("张三");cout << s.getName() << endl;  // 输出:张三return 0;
}

Student::Student() : Person("") { }
这是 Student 的无参构造函数,写法表示:

  • 当创建 Student 对象时,先调用基类 Person 的构造函数,传入空字符串 "" 初始化 Person 部分。
  • 然后执行 Student 自己的构造函数体(这里为空)。

Student::Student(std::string name) : Person(name) { }
这是带参数的构造函数,表示:

  • 创建 Student 对象时,先调用基类 Person 的构造函数,传入参数 name
  • 然后执行 Student 自己的构造函数体(这里为空)。

如果基类没有无参构造函数,编译器就不知道用什么参数去初始化基类部分,编译会失败。 所以派生类构造函数中必须用初始化列表显示调用基类构造函数,告诉它该怎么初始化基类。

3、调用顺序原因
一、构造函数调用顺序:先基类后派生类
  • 当你创建一个派生类对象时,派生类通常会用到基类的成员(包括数据和方法)。
  • 如果基类还没初始化,派生类就无法安全使用基类的内容。
  • 所以必须先调用基类的构造函数,完成基类部分的初始化,再调用派生类构造函数来初始化派生类自己新增的成员。

这样做保证了派生类拥有一个“完整且有效”的基类部分,避免使用未初始化数据带来的错误。


二、析构函数调用顺序:先派生类后基类
  • 对象销毁时,派生类先清理自己新增的资源(比如动态申请的内存、打开的文件等)。
  • 清理完派生类资源后,再去销毁基类成员。
  • 如果先销毁基类,派生类成员还没清理完,就会出现访问已销毁资源的错误。

所以析构时先调用派生类析构函数释放派生类资源,再调用基类析构函数释放基类资源,符合“从内到外”的释放原则。

三、简单比喻 ---- 把对象想象成建房子

构造(建房子)
盖房子的时候,先打好地基(基类),确保基础稳固,
然后再盖楼层(派生类),一层一层往上建。
先有地基,楼层才能安全搭建。

析构(拆房子)
拆房子时,先拆楼层(派生类),再拆地基(基类),
这样避免楼层倒塌砸到地基,也保证拆除顺序安全有序。

http://www.xdnf.cn/news/17657.html

相关文章:

  • Windows执行kubectl提示拒绝访问【Windows安装k8s】
  • `sk_buff` 结构体详解(包含全生命周期解析)
  • 数学建模:控制预测类问题
  • 全面了解机器语言之kmeans
  • 010601抓包工具及证书安装-基础入门-网络安全
  • 【Matplotlib】中文显示问题
  • 企业级WEB应用服务器TOMCAT — WEB技术详细部署
  • 正点原子esp32s3探测土壤湿度
  • openpnp - 顶部相机如果超过6.5米影响通讯质量,可以加USB3.0信号放大器延长线
  • Effective C++ 条款34:区分接口继承和实现继承
  • 数据库面试题集
  • DFT的几点理解(二)
  • 计算二分类误差时的常见错误及解决方案
  • 农经权二轮延包—已有软件与后续研究
  • Spring之【详解AOP】
  • NLP 2025全景指南:从分词到128专家MoE模型,手撕BERT情感分析实战(第四章)
  • scanpy单细胞转录组python教程(三):单样本数据分析之数据标准化、特征选择、细胞周期计算、回归等
  • 制动电阻烧损记录学习
  • Spark执行计划与UI分析
  • JVM调优好用的内存分析工具!
  • jvm有哪些垃圾回收器,实际中如何选择?
  • 工业相机选择规则
  • leetcode经典题目——单调栈
  • 机器学习第八课之K-means聚类算法
  • Android 16 KB页面大小适配的权威技术方案总结
  • Android Camera 打开和拍照APK源码
  • Suno API V5 全面升级——多语言接入,开启 AI 音乐创作新时代
  • GPT‑5 重磅发布
  • 【开源】分层状态机(HFSM)解析:复杂逻辑的清晰表达与FPGA实现(附完整的Verilog交通灯案例及仿真)
  • Loki+Alloy+Grafana构建轻量级的日志分析系统