《C++ primer plus》精炼(OOP部分)——对象和类(8)

学习是一项持续的投资,永远不白费——本杰明·富兰克林

文章目录

  • 第13章:类继承
    • 一个基类和派生类
    • 公有继承的逻辑关系:is-a
    • 多态公有继承

第13章:类继承

一个基类和派生类

从一个类派生出另一个类时,原始类称为基类,继承类称为派生类。以下是一个简单的基类:

class Person
{
private:string name;
public:Person(){name="none";};Person(string s){name=s;}~Person(){};void show(){cout<<name;}
}

这是一个表示人的简约的类,接下来我们用这个人来派生一个学生类:

class Student:public Person
{
private:int grade;
public:Student():Person(){grade=0;}Student(string s,int g):Person(s){grade=g;}void s_show(){show();cout<<grade;}
}
  1. 要使用类继承,在类声明中需要使用如下格式:

class 派生类:public/protected/private 基类

其中public表示公有继承,protected表示保护继承,private表示私有继承,在本章我们只考虑公有继承。

  1. 在继承中,派生类自动包含基类的所有属性和方法,但派生类中的方法不能直接访问基类的私有成员,而要通过基类提供的方法来访问,例如:
Student():Person()
{grade=0;
}

这是Student类的默认构造函数。对于派生类和基类的逻辑关系,可以用下面这张图来表示:
在这里插入图片描述
所以当要构造一个Student类的时候,从概念上要先构造出其中的Person类,所以要在构造函数执行到函数体之前将Person类初始化,即必须选用列表初始化语法。而且因为派生类不能直接访问基类的私有属性,因此下面的代码是错误的:

Student():name("none")//不能直接访问name属性
{grade=0;
}
  1. 使用时,可以用基类指针来指向派生类:
int main(void)
{Person* p[2];p[0]=Person();//指向一个Person类对象p[1]=Student();//也可以指向一个Student类对象
}

注意,这一规则是单向的,也就是说不能把派生类指针指向基类,这是因为编译器看到Person类指针后,会认为这个指针指向的对象拥有Person类声明的方法和属性,下面的代码显然是错误的:

int main(void)
{Student* s[2];s[0]=Student();//可以通过编译s[1]=Person();//不能通过编译s[0]->s_show();//因为s[0]指向的是一个Student类对象,自然可以调用这个方法s[1]->s_show();//但s[1]指向一个Person对象,因此显然是不行的

另外,如果用Person类指针指向了Student类对象,也只能用对象中从Person类继承下来的方法:

int main(void)
{Person* p[2];p[0]=Person();//指向一个Person类对象p[1]=Student();//也可以指向一个Student类对象p[1]->s_show();//错误,不能通过编译,即使指向一个Studnet类对象
}

对于形参为指向基类的指针的函数,也可以用派生类的地址作为实参,因此也可以用派生类对象来初始化基类对象(实际上是使用了复制构造函数),实际效果为将派生类对象中的基类部分拷贝到新的基类对象上。

公有继承的逻辑关系:is-a

当派生类is a基类时,适合使用公有继承,这就是公有继承的逻辑关系,例如在上面的例子中,Student is a Person.
在这里插入图片描述

多态公有继承

在弄清Person类和Student类的关系后,我们可以把这种关系扩展到其他职业:
在这里插入图片描述
同样的,因为teacher is a person,headmaster is a person,也可以使用公有继承。可以试着自己写一写代码,笔者的代码如下:

class Person
{
private:string name;
public:Person(){name="none";};Person(string s){name=s;}virtual ~Person(){};virtual void show(){cout<<name;}
}class Student:public Person
{
private:int grade;
public:Student():Person(){grade=0;}Student(string s,int g):Person(s){grade=g;}virtual void show(){Person::show();cout<<grade;}
}class Teacher:public Person
{
private:int number;//负责哪个班级的班号,简单即可
public:Teacher():Person(){number=0;}Teacher(string s,int n):Person(s){number=n;}virtual void show(){Person::show();cout<<number;}
}class HeadTeacher:public Person
{
private:int s_number;//负责哪个学校的代号
public:HeadTeacher():Person(){number=0;}HeadTeacher(string s,int n):Person(s){s_number=n;}virtual void show(){Person::show();cout<<s_number;}
}

这里需要注意的就是virtual关键字。被virtual关键字当要将基类的方法在派生类中重写时,可以使用这个关键字。当使用这个关键字时,如果一个对象由指针引用,那么这个对象调用函数时,所调用的函数由这个对象的实际类决定。另外,因为进行了函数重写,因此在调用基类的方法时需要使用::运算符。下面是这四个类的测试代码:

Person* p[4];
p[0]=Person();
p[1]=Student();
p[2]=Teacher();
p[3]=HeadTeacher();p[0]->show();//调用Person类中的show函数
p[1]->show();//调用Student类中的show函数
p[2]->show();//调用Teacher类中的show函数
p[3]->show();//调用HeadTeacher类中的show函数

另外,如果将函数的声明和定义分开,那么virtual关键字只在类声明的时候加上即可。
用一个基类指针的数组指向不同派生类的对象,当执行同样的代码时,实际行为会随被执行的对象类型而变化,这就是多态。多态保证了代码的重用性,使得程序员不需要频繁修改测试代码,所以多态也是继封装和继承后OOP中第三个重要的思想。
最后,在Person类中的构造函数也被声明为虚函数,这是因为如果不这样声明,那么编译器会按照指针类型而非实际对象类型来析构数组p所指向的对象,这会导致内存泄漏,所以即使Person类析构函数不进行任何操作,也要声明为虚函数。
请添加图片描述
我是霜_哀,在算法之路上努力前行的一位萌新,感谢你的阅读!如果觉得好的话,可以关注一下,我会在将来带来更多更全面的知识讲解!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/148002.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【kubernetes】kubernetes中的Controller

1 什么是Controller&#xff1f; kubernetes采用了声明式API&#xff0c;与声明式API相对应的是命令式API&#xff1a; 声明式API&#xff1a;用户只需要告诉期望达到的结果&#xff0c;系统自动去完成用户的期望命令式API&#xff1a;用户需要关注过程&#xff0c;通过命令一…

Scapy样例三则

1. 演示ls()/lsc()用法: ##Exec1.pyfrom scapy.all import *## 列出scapy支持的命令 def ListScapyCmd():lsc()## 列出指定协议的各个字段, 用于构成packet def ListProtocolField(protoclName):ls(protoclName)if __name__ "__main__":print("\nexample of …

SNAP与Sen2Cor下载与安装

SNAP软件下载与安装 一、下载地址 首先进入网站 找到DOWNLOAD下载页&#xff0c; 安装完成后&#xff0c;界面如下 还需要再装一个Sen2cor下载好之后&#xff0c;解压到用户文件夹下 然后打开L2A_Process.bat文件 打开CMD&#xff0c;输入 cd C:\Users\lenovo\AppData\L…

ubuntu 安装 flowiseai

flowiseai 可以快速的搭建AI应用 安装docker 安装docker https://docs.docker.com/desktop/install/linux-install/ 安装docker-compose 安装docker-compose https://blog.csdn.net/sunyuhua_keyboard/article/details/133070011?csdn_share_tail%7B%22type%22%3A%22blo…

安装matplotlib_

安装pip 安装matplotlib 安装完毕 导入出现bug......

C++人事管理系统

一、设计目的 企业员工管理系统主要是针对企业员工的基本信息进行增、删、改、查的相关操作&#xff0c;以便用户使用本管理系统时可以快速对企业员工的信息进行管理。 二、设计内容 1.用户首次使用本系统时进行密码设置和初始化操作。 2.实现添加功能&#xff0c;即添加员工…

httpserver 下载服务器demo 以及libevent版本的 httpserver

实现效果如下&#xff1a; 图片可以直接显示 cpp h 这些可以直接显示 其他的 则是提示是否要下载 单线程 还有bug 代码如下 先放上来 #include "httpserver.h" #include "stdio.h" #include <stdlib.h> #include <arpa/inet.h> #include…

为什么Spring不建议使用基于字段的依赖注入

在我们通过IDEA编写Spring的代码的时候&#xff0c;假如我们编写了如下代码&#xff1a; IDEA会给我们一个warning警告&#xff1a; 翻阅官方文档&#xff1b;我们会发现&#xff1a; 大意就是强制依赖使用构造器注入&#xff0c;可选依赖使用setter注入那么这是为什么呢&am…

一键智能视频语音转文本——基于PaddlePaddle语音识别与Python轻松提取视频语音并生成文案

前言 如今进行入自媒体行业的人越来越多&#xff0c;短视频也逐渐成为了主流&#xff0c;但好多时候是想如何把视频里面的语音转成文字&#xff0c;比如&#xff0c;录制会议视频后&#xff0c;做会议纪要&#xff1b;比如&#xff0c;网课教程视频&#xff0c;想要做笔记&…

【算法|动态规划No.10】leetcode LCR 089. 打家劫舍 LCR 090. 打家劫舍 II

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…

fcpx插件:82种复古电影胶卷框架和效果mFilm Matte

无论您是在制作音乐剪辑、私人假期视频还是大型广告活动&#xff0c;这个专业的插件都将帮助您为您的镜头赋予真正的电影角色。 复古效果在任何视频中都能立即识别出来&#xff0c;增添了感伤的复古氛围&#xff0c;并使镜头更具说服力。使用 mFilm Matte 轻松实现这些特征&…

C++算法 —— 动态规划(10)二维费用背包

文章目录 1、动规思路简介2、一和零3、盈利计划 背包问题需要读者先明白动态规划是什么&#xff0c;理解动规的思路&#xff0c;并不能给刚接触动规的人学习。所以最好是看了之前的动规博客&#xff0c;以及两个背包博客&#xff0c;或者你本人就已经懂得动规了。 1、动规思路简…

九、2023.10.3.Linux(end).9

文章目录 33、简述mmap的原理和使用场景&#xff1f;34、互斥量能不能在进程中使用&#xff1f;35、协程是轻量级线程&#xff0c;轻量级表现在哪里&#xff1f;36、说说常见信号有哪些&#xff0c;表示什么含义&#xff1f;37、说说线程间通信的方式有哪些&#xff1f;38、说说…

Access注入---Cookie注入

Access注入----Cookie注入Access数据库&#xff08;微软&#xff09; 逐渐淘汰 &#xff08;没有库的概念&#xff0c;是表的集合&#xff09;Access没有系统自带库Cookie注入&#xff08;头注入HEAD注入的&#xff09;php中产生Cookie注入的可能性小&#xff0c;但ASP产生Cook…

RabbitMQ的基本介绍

什么是MQ 本质是一个队列&#xff0c;只不过队列中存放的信息是message罢了&#xff0c;还是一种跨进程的通信机制&#xff0c;用于上下游传递信息。在互联网架构中&#xff0c;MQ是一种非常常见的上下游“逻辑解耦物理解耦”的消息通信服务。使用了MQ之后&#xff0c;信息发送…

postgresql16-新特性

postgresql16-新特性 any_value数组抽样数组排序 any_value any_value 返回任意一个值 select e.department_id ,count(*), any_value(e.last_name) from cps.public.employees e group by e.department_id ;数组抽样 -- 从数组中随机抽取一个元素 array_sample(数组&#…

以太网基础学习(四)——IP协议

一 、IP协议概述 IP&#xff08;Internet Protocol&#xff0c;互联网协议&#xff09;是互联网通信的基础协议&#xff0c;它负责将数据包从源地址传输到目的地址。IP协议定义了如何封装数据包&#xff0c;如何寻址数据包以及如何路由数据包&#xff0c;它是随着互联网的出现而…

弧度、圆弧上的点、圆的半径(r)、弧长(s)之间的关系

要计算弧度和圆弧上的点&#xff0c;需要知道以下几个要素&#xff1a; 圆的半径&#xff08;r&#xff09;&#xff1a;即圆的中心到圆周上任意一点的距离。 弧长&#xff08;s&#xff09;&#xff1a;从圆周上的一个点到另一个点所经过的弧长。 弧度&#xff08;θ&#x…

【CAD二次开发】给CAD添加TRUSTEDPATHS避免dll插件信任弹窗

找到配置文件目录,遍历下面的每个配置文件; 找到 Variables 下的TRUSTEDPATHS项目;在后面添加新的目录即可,多个目录使用分号分隔; public static void AddPath(string trusedPath){// 指定注册表键的路径

指针笔试题(带解析版)

题目2&#xff1a; struct MyStruct {int num;char* pcname;short sdate;char cha[2];short sba[4]; }*p; //结构体大小为32字节 //p0x100000 int main() {p 0x100000;printf("%p\n", p 0x1);//p&#xff1a;结构体指针&#xff0c;1下一个结构体指针&#xff0c;…