c++的学习之路:20、继承(1)

摘要

本章主要是讲以一下继承的一些概念以及使用方法等等。

目录

摘要

一、继承的概念及定义

1、继承的概念

2、继承定义

1.2.1、定义格式

1.2.2、继承关系和访问限定符

1.2.3、继承基类成员访问方式的变化

3、总结

二、基类和派生类对象赋值转换

三、继承中的作用域

四、派生类的默认成员函数

五、继承与友元

六、继承与静态成员

七、代码

 八、思维导图


一、继承的概念及定义

1、继承的概念

继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保
持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象
程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继
承是类设计层次的复用。

这个继承形容的十分形象,就像子承父业继承家产,哈哈这个就是继承,但是呢在之前说类的时候就说了有三个类型,public、private、protected这三个类型,但是protected我却没咋用过,这里就使用,下面将会一一介绍。

2、继承定义

1.2.1、定义格式

如下方代码就是一个继承的定义,如图二就是测试结果,图一就是继承定义格式,这里public也可以不写,但是默认是私密形式,但是不也可以用struct就是共有的这里和类的定义有点相像。

class Person
{
protected:
    string _name = "张三";
    int _age = 18; 
};

class Student :public Person
{
protected:
    int _stuid;
};

1.2.2、继承关系和访问限定符

如下方两个导图就是所有的继承关系和访问限定符

1.2.3、继承基类成员访问方式的变化

下面表就是继承的基类成员访问方式的变化,就是根据这个表去写的权限。

类成员/继承方式public继承protected继承private继承
基类的public成员派生类的public成员派生类的protected
成员
派生类的private
成员
基类的protected
成员
派生类的protected
成员
派生类的protected
成员
派生类的private
成员
基类的private成
在派生类中不可见在派生类中不可见在派生类中不可

3、总结

1. 基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。

2. 基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的。

3. 实际上面的表格我们进行一下总结会发现,基类的私有成员在子类都是不可见。基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),public  > protected> private。

4. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式。

5. 在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强。

二、基类和派生类对象赋值转换

派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片
或者切割。寓意把派生类中父类那部分切来赋值过去。基类对象不能赋值给派生类对象。

基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类
的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用RTTI(Run-Time Type Information)的dynamic_cast 来进行识别后进行安全转换。

如下图所示这种切割方式,就是上述所说的切片。

三、继承中的作用域

1. 在继承体系中基类和派生类都有独立的作用域。

2. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)

3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。

4. 注意在实际中在继承体系里面最好不要定义同名的成员

如下方代码想要访问B里面的fun就会访问不到,所以这里就可以看到下方代码中main的方式就可以访问了,结果如图。

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;
    }
};

int main()
{
    /*Student s;*/
    B b;
    b.fun(10);
}

四、派生类的默认成员函数

6个默认成员函数,“默认”的意思就是指我们不写,编译器会变我们自动生成一个,那么在派生类
中,这几个成员函数是如何生成的呢?

1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。

2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。

3. 派生类的operator=必须要调用基类的operator=完成基类的复制。

4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。

5. 派生类对象初始化先调用基类构造再调派生类构造。

6. 派生类对象析构清理先调用派生类析构再调基类的析构。

7. 因为后续一些场景析构函数需要构成重写,重写的条件之一是函数名相同(这个我们后面会讲解)。那么编译器会对析构函数名进行特殊处理,处理成destrutor(),所以父类析构函数不加virtual的情况下,子类析构函数和父类析构函数构成隐藏关系。

下面的导图就是六个默认成员函数。

五、继承与友元

友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员,这中的解决方式也简单就是在子类在进行声明一下是友元就可以了,如下方代码所示,就是接着声明,要不就会使用不了。

class Person
{
public:
    friend void test(const Person& p, const Student& s);
protected:
    string _name; // 姓名
};
class Student : public Person
{
public:
    friend void test(const Person& p, const Student& s);
protected:
    int _stuNum; // 学号
};

六、继承与静态成员

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例 ,这样就可以计算使用了多少次这个函数,可以用来测试创建了多少人,如下方代码和图片所示

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()
{
    /*Student s;*/
    /*B b;
    b.fun(10);*/
    Student s1;
    Student s2;
    Student s3;
    Graduate s4;
    cout << " 人数 :" << Person::_count << endl;
    
}

七、代码

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <vector>
#include <string>
using namespace std;//class Person
//{
//protected:
//	string _name = "张三";
//	int _age = 18; 
//};
//
//class Student :public Person
//{
//protected:
//	int _stuid;
//};
//
//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;
//	}
//};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; 
};//class Person
//{
//public:
//	friend void test(const Person& p, const Student& s);
//protected:
//	string _name; // 姓名
//};
//class Student : public Person
//{
//public:
//	friend void test(const Person& p, const Student& s);
//protected:
//	int _stuNum; // 学号
//};int main()
{/*Student s;*//*B b;b.fun(10);*/Student s1;Student s2;Student s3;Graduate s4;cout << " 人数 :" << Person::_count << endl;}

 八、思维导图

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

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

相关文章

Matlab自学笔记二十九:元胞数组的创建、索引和转换方法

1.概念 元胞数组&#xff08;cell array&#xff09;是一种具有容器特性的数据类型&#xff0c;每个元素可以包含任何类型的数据&#xff1b; 2.创建方法 &#xff08;1&#xff09;方法一&#xff1a;使用大括号{}&#xff1b; &#xff08;2&#xff09;方法二&#xff1…

9【原型模式】复制一个已存在的对象来创建新的对象

你好&#xff0c;我是程序员雪球。 今天我们来学习23种设计模式之原型模式&#xff0c;在平时开发过程中比较少见。我带你了解什么是原型模式&#xff0c;使用场景有哪些&#xff1f;有什么注意事项&#xff1f;深拷贝与浅拷贝的区别&#xff0c;最后用代码实现一个简单的示例…

大数据深度学习:基于Tensorflow深度学习卷积神经网络CNN算法垃圾分类识别系统

文章目录 大数据深度学习&#xff1a;基于Tensorflow深度学习卷积神经网络CNN算法垃圾分类识别系统一、项目概述二、深度学习卷积神经网络&#xff08;Convolutional Neural Networks&#xff0c;简称CNN&#xff09;三、部分数据库架构四、系统实现系统模型部分核心代码模型训…

Android之图片压缩几种方式

大概可以分为以下几类&#xff1a;更换图片格式&#xff0c;质量压缩&#xff0c;采样率压缩&#xff0c;缩放压缩&#xff0c;调用jpeg压缩等 1.设置图片格式 Android目前常用的图片格式有png&#xff0c;jpeg和webp&#xff0c; png&#xff1a;无损压缩图片格式&#xff0…

【Java】新手一步一步安装 Java 语言开发环境

文章目录 一、Windows 10 系统 安装 JDK8二、 Mac 系统 安装 JDK8三、IDEA安装 一、Windows 10 系统 安装 JDK8 &#xff08;1&#xff09;打开 JDK下载网站&#xff0c;根据系统配置选择版本&#xff0c;这里选择windows 64位的版本&#xff0c;点击下载&#xff08;这里需要…

5.SpringSpringBoot八股

Spring,Spring MVC,Spring Boot 之间什么关系? Spring就是整个Spring框架的整体&#xff0c;包含AOP、JDBC、Spring MVC等等模块 SpringBoot是Spring的精简版&#xff0c;它在Spring的基础上添加了自动装配、内置tomcat服务器等功能&#xff0c;使得代码量更少&#xff0c;同…

Finetuning vs. Prompting:大语言模型两种使用方式

目录 前言1. 对于大型语言模型的两种不同期待2. Finetune(专才)3. Prompt(通才)3.1 In-context Learning3.2 Instruction-tuning3.3 Chain of Thought(COT) Prompting3.4 用机器来找Prompt 总结参考 前言 这里和大家分享下关于大语言模型的两种使用方式&#xff0c;一种是 Fine…

2024最新 PyCharm 2024.1 更新要点汇总

2024最新 PyCharm 2024.1 更新要点汇总 文章目录 2024最新 PyCharm 2024.1 更新要点汇总摘要引言 Hugging Face&#xff1a;模型和数据集的快速文档预览针对 JavaScript 和 TypeScript 的全行代码补全 PyCharm Professional编辑器中的粘性行编辑器内代码审查新终端 Beta新的 AI…

ip连接检测

1.在C#中&#xff0c;你可以使用Socket类来检测IP连接是否正常。以下是一个简单的示例代码&#xff0c;演示了如何使用Socket类来检测IP连接的可用性&#xff1a; using System; using System.Net; using System.Net.Sockets;class Program {static void Main(string[] args){…

js+网络摄像头实现人体肢体关键点动作捕获

最近有一个项目&#xff0c;客户需要用户人体姿势识别&#xff0c;进行表演考核用途&#xff0c;或者康复中心用户恢复护理考核&#xff0c;需要用摄像头进行人体四肢进行肢体关键点对比考核&#xff0c;资料还是太少了。只有个别大佬发了部分技术指导。感觉写的不错。 阿里云…

【微信小程序——案例——本地生活(列表页面)】

案例——本地生活&#xff08;列表页面&#xff09; 九宫格中实现导航跳转——以汽车服务为案例&#xff08;之后可以全部实现页面跳转——现在先实现一个&#xff09; 在app.json中添加新页面 修改之前的九宫格view改为navitage 效果图&#xff1a; 动态设置标题内容—…

PTA(题目集三 题目 代码 C++ 注解)

目录 题目一&#xff1a; 代码&#xff1a; 题目二&#xff1a; 代码&#xff1a; 题目三&#xff1a; 代码&#xff1a; 题目四&#xff1a; 代码&#xff1a; 题目五&#xff1a; 代码&#xff1a; 题目六&#xff1a; 代码&#xff1a; 题目七&#xff1a; 代码…

【QT+QGIS跨平台编译】161:【qgispython跨平台编译】—【qgis_python.h生成】

点击查看专栏目录 文章目录 一、qgis_python.h介绍二、信息分析三、qgis_python.h生成一、qgis_python.h介绍 qgis_python.h 是 QGIS(Quantum Geographic Information System)GIS 软件的一个头文件。QGIS 是一个开源的地理信息系统软件,提供了丰富的地图制图和空间分析功能。…

第四百五十九回

文章目录 1. 概念介绍2. 方法与细节2.1 获取方法2.2 使用细节 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何获取当前系统语言"相关的内容&#xff0c;本章回中将介绍如何获取时间戳.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在本章…

Arduino IDE开发esp8266,如何调试

在Arduino IDE中开发ESP8266时,调试通常需要依赖于串行输出(Serial.print 语句)和LED指示灯。由于ESP8266没有像一些高级开发平台那样的内置调试功能(例如JTAG或SWD接口),所以调试可能较为基础。以下是一些用于Arduino IDE中ESP8266开发调试的基本步骤: 环境准备:首先,…

一款绿联VGA转HDMI转换器MS9288A方案

实物 该转换器在后备箱放了一段时间&#xff0c;就成这个样子了&#xff0c;当然&#xff0c;后备箱也比较恶劣&#xff0c;堪比盐雾试验&#xff0c;因为有瓶稀盐酸倒了&#xff0c;发现不及时&#xff0c;一个新的转换器就成这个样子了。 VGA转HDMI转换器VGA输入插头 VGA转…

Go语言中常见的JSON处理错误

1. 由嵌入式字段导致的非预期行为 在下面的例子中,我们创建一个Event结构体,其包含一个ID和一个嵌入式的时间戳: type Event struct {ID inttime.Time } 由于 time.Time 是嵌入式的,因此我们可以直接通过Event来访问 time.Time 方法,比如event.Second( )。 嵌入式字段…

【Leetcode|#88.合并两个有序数组】

题目 给你两个按非递减顺序排列的整数数组nums1和nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示nums1和nums2中的元素数目。 请你合并nums2到nums1中&#xff0c;使合并后的数组同样按非递减顺序排列。 注意&#xff1a;最终&#xff0c;合并后数组不应由函数返…

嵌入式MCU BootLoader开发配置详细笔记教程

目录 一、BootLoader基础 二、BootLoader原理及配置 三、BootLoader程序 bootloader.h bootloader.c 四、Application1 用户程序 application1.h application1.c 五、Application2 用户程序 application2.h 六、程序运行效果 七、工程文件Demo 一、BootLoader基础 …

HTTP Keep-Alive的作用

作用&#xff1a;Keep-Alive&#xff1a;使客户端到服务器端的连接持续有效&#xff0c;当出现对服务器的后继请求时&#xff0c; Keep-Alive功能避免了建立或者重新建立连接。Web服务器&#xff0c;基本上都支持HTTP Keep- Alive。 缺点&#xff1a;对于提供静态内容的网站来…