【C++】12.继承
创始人
2025-05-29 20:02:52

1.引入继承

学生管理系统

  • 学生

  • 老师

  • 社管阿姨

  • 保安大叔

    4个类 4个类有很多重复的东西

    name age telephone ...等

    继承 Person->老师 学生等

从这里我们可以看到继承目的:类之间的复用

2.继承

1°定义

class Student(派生类):public(继承方式) Person(基类)

{};

2°访问方式

#include 
using namespace std;class Person
{
public:void Print(){cout << "name:" << _name << endl;cout << "age:" << _age << endl;}
protected:string _name = "peter";//姓名int _age = 18;//年龄
};//继承
//private和protected在当前类无差别
//在继承的子类有差别 
//继承下来的private不可用 protected可以用
//谁的范围小就取谁
//public>protected>private
//实际当中都用public继承 几乎不使用protected/private
class Student :public Person
{
protected:int _stuid;//学号
};class Teacher :public Person
{
protected:int _jobid;//工号
};int main()
{Student s;Teacher t;s.Print();t.Print();return 0; 
}

3.切片

  • 派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用->切片

  • 而基类对象不能赋值给派生类对象 范围大的可以给范围小的

  • 基类的指针可以通过强制类型转换赋值给派生类的指针。但是必须是基类的指针是指

    向派生类对象时才是安全的

class Person
{
protected:string _name; //姓名string _sex;  //性别	int _age;	  //年龄
};class Student :public Person
{
public:int _No;//学号
};int main()
{Person p;Student s;//子类和父类之间的赋值兼容规则//1.子类对象可以赋值给父亲对象/指针/引用p = s;Person* ptr = &s;Person& ret = s;//切片//自己的_No不给 其他继承的可以赋值//指针和别名也是 只会有部分的指针和别名//2.反过来行不行?//s=p;绝对不行//3.Student* sptr=(Student*)ptr;//有可能可以 这个父类的指针指向子类对象v return 0;

4.继承中的作用域

就近:如果两个变量是一样的不会报错 先找自己类里的变量 再找父类里的变量

如果就想访问父类的同名变量的话 加上Person::进行限定

class Person
{
protected:string _name = "小李子"; // 姓名int _num = 111; // 身份证号
};
class Student : public Person
{
public:void Print(){cout << " 姓名:" << _name << endl;cout << " 身份证号:" << Person::_num << endl;//指定访问父类cout << " 学号:" << _num << endl;//默认访问子类}
protected:int _num = 999; // 学号
};
void Test()
{Student s1;s1.Print();
}int main()
{Test();return 0;
}
//这两个num不会报错 因为两个num在不同的作用域
//那么访问num的时候到底是父类的num还是子类的nums
//子类的num 就近原则 先找自己类里面 再找其他类
//当父类和子类同时有同名成员时,子类的成员隐藏了父类的成员
//那假设就想访问父类怎么办
//指定 Person::_num

  • 练习

  • A和B的fun构成什么关系

    a.重载 b.重写 c.重定义 d.编译不通过/以上都不对

class A
{
public:void fun(){cout << "func()" << endl;}
};
class B : public A
{
public:void fun(int i) {A::fun();cout << "func(int i)->" << i << endl;}
};
void Test()
{B b;b.fun(10);//b.fun();//调父类调不动      不可以//b.A::fun();//指定去调父类的 可以
};

很容易选到重载 应该是c 重定义 也就是隐藏

隐藏不是说调不动 而是要指定父类去调用

重载的要求:必须在同一作用域

5.派生类的默认成员函数

  • 派生类的默认成员函数是如何生成的?
  1. 构造:必须调用基类的构造函数初始化基类的那一部分成员 如果基类没有构造函数 构造时必须显示调用(加上作用域)
  2. 拷贝构造:必须调用基类的拷贝构造
  3. 赋值:必须调用基类的operator=
  4. 析构:派生类的析构函数调用完后自动调用基类的析构函数 保证正确顺序
  5. 派生类对象初始化先调用基类构造再调派生类构造
  6. 派生类对象析构清理先调用派生类析构再调基类的析构

#include 
using namespace std;class Person
{
public:Person(const char* name = "peter")//构造: _name(name){cout << "Person()" << endl;}Person(const Person& p)//拷贝构造: _name(p._name){cout << "Person(const Person& p)" << endl;}Person& operator=(const Person& p)//赋值{cout << "Person operator=(const Person& p)" << endl;if (this != &p)_name = p._name;return *this;}~Person()//析构{cout << "~Person()" << endl;}
protected:string _name; // 姓名
};//子类继承的父类 父类初始化就调用父类的构造函数
//子类不写默认函数 会调父类的默认构造函数 全缺省可以调
class Student :public Person
{
public:Student(const char* name, int stuid):Person(name)//指定,_stuid(stuid){cout << "Student(const char* name, int stuid)" << endl;}Student(const Student& s):Person(s)//除了stuid 其余的继承父类 先调用父类的 再调用自己的,_stuid(s._stuid){cout << "Student(const Student& s)" << endl;}Student& operator=(const Student& s){if (this != &s){Person::operator=(s);//调父类 完成切片 隐藏了父类 所以要指定作用域 调父类的时候全部需要指定_stuid = s._stuid;//自己成员cout << "Student& operator=(const Student& s)" << endl;}return *this;}~Student(){//还是需要指定//Person::~Person();//子类的析构函数和父类的析构函数构成隐藏 因为他们的名字会被编译器统一处理成destructor(跟多态有关)//析构时会发现有三个析构 多一个Person析构 应该去掉第一个Person析构//不需要 析构函数会自动调用//结束时会自动调用父类的析构函数 因为这样才能保证先析构子类后析构父类//不需要显示的调 因为显示的调会多出一个析构 而且析构顺序不对cout << "~Student()" << endl;}
protected:int _stuid;
};int main()
{//Student s1("jack",1);//Student s2(s1);//不写也会调用父类的拷贝构造 写了后先调父类 再调子类//Student s3 = s1;//Student s4("rose", 2);//析构时会发现有三个析构 多一个Person析构 应该去掉第一个Person析构return 0;
}

s1

s2

s3

s4

  • 派生类实现默认成员函数时 先要调用基类相关函数把基类那一部分成员实现功能(需

    要指定基类) 再实现自己类里面的成员功能

  • 析构函数比较特殊 不需要去实现 因为调用基类的析构函数的话 基类会先析构 然后派

    生类再析构 基类会再次析构**(当派生类析构后 基类会自动调用析构函数 保证析构的顺**

    序)

  • 除了析构以外 全部都需要显示调用父类 相当于切片那一段直接找父类的就行

    然后子类自己的成员自己处理

  • 如何设计一个不能被继承的类?

构造私有 析构私有

继承后不可见私有的 子类调不动父类的构造和析构 子类对象都生成不了 所以不能被

继承

6.继承和友元

父类中友元关系不能被继承下来

7.基类与静态成员

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少

个子类,都只有一个static成员实例。

#include 
using namespace std;class Person
{public:Person() { ++_count; }protected:string _name; // 姓名public:static int _count; // 统计人的个数。
};
int Person::_count = 0;
class Student : public Person
{
protected:int _stuNum; // 学号
};
class Graduate : public Student
{
protected:string _seminarCourse; // 研究科目
};int main()
{Person p;Student s;p._count = 1;s._count = 2;Person::_count++;cout << Person::_count << endl;cout << Student::_count << endl;//派生了多少子类 静态成员只有一个return 0;
}

_count都是3

8.菱形继承

1°继承类型

  • 单继承:一个子类只有一个直接父类时称这个继承关系为单继承

  • 多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承

  • 菱形继承:菱形继承是多继承的一种特殊情况

  • 菱形继承的问题:

    **Assistant会有两份相同数据导致数据冗余 两份数据到底用哪份 产生歧义(二义性) **

    需要指定是哪个 但还是有数据冗余的问题

1.数据冗余

2.二义性

  • 如何解决菱形继承问题?

    官方引入了virtual继承 虚继承 解决了数据冗余和二义性

class Person
{
public:string _name; // 姓名
};
class Student : virtual public Person//添加virtual
{
protected:int _num; //学号
};
class Teacher : virtual public Person//添加virtual
{
protected:int _id; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修课程
};
void Test()
{// 这样会有二义性无法明确知道访问的是哪一个Assistant a;a._name = "peter";//不加virtual会报错// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决a.Student::_name = "xxx";a.Teacher::_name = "yyy";
}
  • C++的缺陷有哪些?

多继承就是一个问题->菱形继承->虚继承->底层结构的对象模型非常复杂 且有一定

效率损失

  • 什么是菱形继承?

多继承的一种特殊情况

  • 菱形继承问题是什么?

数据冗余和二义性

  • 如何解决菱形继承?

虚继承

  • 那么解决原理是什么?

需要了解内存对象模型 也就是对象在内存中是怎么存的

2°内存对象模型

class A
{
public:int _a;
};
// class B : public A
class B : virtual public A
{
public:int _b;
};
// class C : public A
class C : virtual public A
{
public:int _c;
};
class D : public B, public C
{
public:int _d;
};
int main()
{D d;cout << sizeof(d) << endl;//8+8+4=20 //加了virtual变为了24d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;d._a = 6;//a放到了公共位置//多了两个像指针一样的东西 变为了24//所以这两个指针是干啥的//两个指针最后表示的是偏移量 多付出了8个字节//存偏移量的表叫虚基表->虚基类return 0;
}

这里是通过了B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚

基表。虚基表中存的偏移量。通过偏移量可以找到下面的A。

通过偏移量地址(指针)->偏移量值->对象

9.总结

  • 多继承是C++的缺陷之一,Java设计的时候避开了多继承

继承与组合

//继承
class A{};
class B:public A
{};//组合
class C{};
class D
{C c;
}

继承是一种白箱(看得见)复用 父类对子类基本是透明的 但是一定程度破坏了父类的封装

组合是一种黑箱(看不见)复用 C对D是不透明的 C保持着封装

对比认为组合是更好的

组合的类耦合度更低

继承的类是一种高耦合

  • 面试:用组合还是继承?

更符合is a 就继承

更符合has a 就组合

都可以 优先使用组合

【C++】12.继承 完

相关内容

热门资讯

Opentss代码测试 这是个多方门限签名库,使用rust实现。 代码包含两个功能,秘密共享密钥...
【id:14】【20分】C. ... 题目描述 编写一个函数比较两个字符串,参数是两个字符指针(要求显式定义...
无需公网IP,远程连接SQL ... 文章目录1.前言2.本地安装和设置SQL Server2.1 SQL Server下载2.2 SQL...
WuThreat身份安全云-T... 漏洞名称:CairoSVG 文件服务器端请求伪造 漏洞级别:严重 漏洞编号:CVE-2023-275...
Postgresql源码(10... 1 子事务控制语句分析 1.1 执行savepoint 执行函数: 【立即执行】→De...
python@日期和时间@da... 文章目录python datetimerelativedeltademo`timedelta...
数据库--进阶版-11--SQ... 1.插入数据 ·insert优化: 例如要插入下面这些 insert into tb_...
C语言—文件操作 为什么使用文件使用文件可以直接将数据存放到电脑硬盘上,做到数据的持久化什么是文件硬盘上...
【Zabbix_6.x 第三章... 文章目录👹 关于作者Zabbix 系列文章目录第三章 监控任意主机一、解决第二章登录...
python爬虫可以爬什么 Python爬虫可以爬取的东西有很多,Python爬虫怎么学?简单的分析...
组态王与200SMART之间无... 在实际的工业场合应用中,人机界面跟PLC配套使用是比较常见的现场“CP组合”ÿ...
KaiwuDB 荣获第三届 I... 3月17日,由中国某部电子化标准研究院、苏州金融科技协会、中国计算机用户协会指导&#x...
上海人工智能企业CIMCAI智... 上海人工智能企业CIMCAI智能港口自动化港口数字化码头智慧港航,成熟终端智慧港航人工...
红米note10 pro机型解... 前言。操作解除锁类案例只限于自己的机型, 因手机号长期不用或者忘记密码导致账号锁出现的...
9、Cascaded Diff... 简介 主页:https://cascaded-diffusion.github.io/...
异核通信框架(1)——SMP和... 0.前言         我是菜鸡,很久没有发表文章了。老样子,今天推荐...
Leetcode.2048 下... 题目链接 Leetcode.2048 下一个更大的数值平衡数 Rating : 17...
GBASE南大通用第二届校园大... 第二届校园大使在本周正式集结完毕啦! 全国一共有27所高校近50余名学生报名参加。 各...
Blender Apps?20... 2023 年对 Blender 来说将会是很有趣的一年,除了努力保持核心功能稳定和不断...
Python数据结构与算法(p... 学习材料清华大学博士讲解Python数据结构与算法 B站:https://www.bi...
Docker安装使用Nacos Docker安装使用Nacos1 参考2 Nacos版本2.1 访问地址3 Docker部署Naco...
Spring Boot 接口统... 需求 需求如题,想给一个 spring boot 项目的所有请求路径添加统一前缀&#x...
Boeing MQ-28 Gh... Boeing MQ-28 Ghost Bat作为五代机忠诚僚机的开山之作,今天我们来一...
线性动态规划问题 文章目录1. 三角形中最小路径之和2. 最长递增子序列3. 最长公共子序列 1. 三角形中最小路径之...
携手共赢!菊风荣获三基同创“2... 近年来智能手表市场发展势头迅猛,为满足用户多样化的应用场景,对于音视频能...
CentOS8提高篇3:Cen... 1. 准备工作(需要配置epel, rpmfusion源); 配置e...
计算机组成原理(7)--哈工大 程序中断方式中断的概念I/O中断的产生CPU和打印机部分并行工作程序中断方式的接口电路配置中断请求触...
Amazon S3 客户端:T... TntDrive 是适用于 Windows 的新 Amazon S3 客户端。使用 TntDrive...
java8 jdk1.8在wi... 一、软件下载 1、从网盘获取 java8安装包 2、或者从官网获取(需要提前注册ora...
【 Deep-Shallow ... A Deep-Shallow Fusion Network with Multi-Detail Ex...