【C++】继承的概念和简单介绍、基类和派生类对象复制转换、继承中的作用域、派生类的默认成员函数

文章目录

  • 继承
    • 1.继承的概念和简单介绍
      • 1.1继承的概念
      • 1.2继承的定义
    • 2.基类和派生类对象复制转换
    • 3.继承中的作用域
    • 4.派生类的默认成员函数
    • 5.继承与友元
    • 6.继承与静态成员

继承

1.继承的概念和简单介绍

1.1继承的概念

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

  在C++中,继承是一种面向对象编程的概念,它允许一个类(称为派生类或子类)从另一个类(称为基类或父类)继承属性和方法。继承使得派生类可以重用基类的代码,并且可以在此基础上添加新的功能或修改现有的功能。

1.2继承的定义

在这里插入图片描述

  (1)公有继承(public inheritance):基类中的公有成员在派生类中仍然是公有的,保持了访问权限不变。派生类可以直接访问基类的公有成员,但不能直接访问基类的私有成员。

class 基类 {
public:
    // 公有成员
};

class 派生类 : public 基类 {
public:
    // 派生类的成员
};


  (2)保护继承(protected inheritance):基类中的公有成员在派生类中变为保护的,保护成员只能在派生类内部或其派生类中访问,外部无法访问。

class 基类 {
protected:
    // 保护成员
};

class 派生类 : protected 基类 {
public:
    // 派生类的成员
};


  (3)私有继承(private inheritance):基类中的公有成员在派生类中变为私有的,私有成员只能在派生类内部访问,外部无法访问。

class 基类 {
private:
    // 私有成员
};

class 派生类 : private 基类 {
public:
    // 派生类的成员
};

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

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


总结:

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

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

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

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

2.基类和派生类对象复制转换

  在C++中,基类和派生类之间的对象复制转换是指将一个基类对象赋值给一个派生类对象或将一个派生类对象赋值给一个基类对象的操作。 这种操作可以通过以下两种方式进行:

  (1)赋值兼容(切片)(slicing):将派生类对象赋值给基类对象时,会发生对象切片。只会复制基类部分的成员,派生类特有的成员将被丢弃。

  (2)引用/指针转换: 可以使用基类的引用或指针来引用或指向派生类对象。这样做可以实现基类和派生类对象之间的相互转换,但是只能访问到基类部分的成员,无法访问到派生类特有的成员。

在这里插入图片描述

class Person
{
protected :
	string _name; // 姓名
    string _sex;  // 性别
    int _age; // 年龄
};

class Student : public Person
{
public :
	int _stuid ; // 学号
};

void Test ()
{
	Student sobj ;
	 // 1.子类对象可以赋值给父类对象/指针/引用
	Person pobj = sobj ;
	Person* pp = &sobj;
 	Person& rp = sobj;
    
	 //2.基类对象不能赋值给派生类对象
    sobj = pobj;
    
    // 3.基类的指针可以通过强制类型转换赋值给派生类的指针
    pp = &sobj
    Student* ps1 = (Student*)pp; // 这种情况转换时可以的。
    ps1->_No = 10;
    
    pp = &pobj;
 	Student* ps2 = (Student*)pp; 
 	// 这种情况转换时虽然可以,但是会存在越界访问的问题
    ps2->_No = 10;
}

总结:

  (1)派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。

  (2)基类对象不能赋值给派生类对象。

  (3)基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。

3.继承中的作用域

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

  (2)子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏也叫重定义。

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

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

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);
};
//此时A和B中的fun构成隐藏,不是重载,因为fun不在同一个作用域
//B中的fun和A中的fun构成隐藏,成员函数满足函数名相同就构成隐藏
//重载需要函数在同一个作用域中,函数名相同,参数列表不同才可以实现


重载和隐藏的区别

  (1)重载(overloading):重载是指在同一个作用域内,可以定义多个具有相同名称但参数列表不同的函数。 重载函数可以根据参数的不同类型或数量来决定调用哪个函数。重载函数的特征是具有相同的函数名,但参数列表不同。

void 函数名(int 参数1);
void 函数名(double 参数1);
void 函数名(int 参数1, int 参数2);
//此时函数实现重载


  (2)隐藏(hiding):隐藏是指在派生类中定义了与基类中同名的成员(变量或函数),从而隐藏了基类的同名成员。 当在派生类中使用同名成员时,会优先访问派生类中的成员,而不是基类中的成员。如果需要访问基类的同名成员,可以使用作用域解析运算符(::)来指定基类的作用域。

class 基类 {
public:
    int 成员变量;
    void 成员函数();
};

class 派生类 : public 基类 {
public:
    int 成员变量; // 隐藏了基类的成员变量
    void 成员函数(); // 隐藏了基类的成员函数
};

派生类 obj;
obj.成员变量; // 访问派生类的成员变量
obj.基类::成员变量; // 访问基类的成员变量
obj.成员函数(); // 调用派生类的成员函数
obj.基类::成员函数(); // 调用基类的成员函数
//此时的成员函数构成隐藏

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

  派生类的默认成员函数是指在派生类中没有显式定义的成员函数,而是通过继承基类的成员函数来自动生成的函数。

  和普通的类一样派生类的默认成员函数包括以下几种:

  (1)默认构造函数(Default Constructor): 如果派生类没有显式定义构造函数,那么编译器会自动生成一个默认构造函数。默认构造函数会调用基类的默认构造函数来初始化基类的成员,然后再初始化派生类的成员。

  (2)拷贝构造函数(Copy Constructor): 如果派生类没有显式定义拷贝构造函数,那么编译器会自动生成一个拷贝构造函数。拷贝构造函数会调用基类的拷贝构造函数来初始化基类的成员,然后再初始化派生类的成员。

  (3)赋值运算符(Assignment Operator): 如果派生类没有显式定义赋值运算符,那么编译器会自动生成一个赋值运算符。赋值运算符会调用基类的赋值运算符来赋值基类的成员,然后再赋值派生类的成员。

派生类的默认成员函数如何生成

  1.构造函数

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

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

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

(4)派生类对象初始化先调用基类构造再调派生类构造。

  2.析构函数

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

(2)派生类对象析构清理,先调用派生类析构,再调基类的析构。

在这里插入图片描述

5.继承与友元

  友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员。

class Student;
class Person
{
public:
	friend void Display(const Person& p, const Student& s);
protected:
	string _name; // 姓名
};

class Student : public Person
{
protected:
	int _stuNum; // 学号
};

void Display(const Person& p, const Student& s)
{
	cout << p._name << endl;
	cout << s._stuNum << endl;
}

void main()
{
	Person p;
	Student s;
	Display(p, s);
}

6.继承与静态成员

  基类定义了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 ; // 研究科目
};

void TestPerson()
{
	Student s1 ;
	Student s2 ;
	Student s3 ;
	Graduate s4 ;
	cout <<" 人数 :"<< Person ::_count << endl;
	
	Student ::_count = 0;
	cout <<" 人数 :"<< Person ::_count << endl;
}

//人数: 4
//人数: 0

当然继承还有很多的知识点,这里只是对C++继承的部分介绍了😉
如有错误❌望指正,最后祝大家学习进步✊天天开心✨🎉

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux系统编程】冯诺依曼体系结构

目录 前言 什么是冯诺依曼体系结构&#xff1f; 冯诺依曼体系结构如何进行数据处理的&#xff1f; 存储器在冯诺依曼体系中有什么作用&#xff1f; 冯诺依曼体系结构为什么要这样设计&#xff1f; 冯诺依曼结构总结 前言 相信对于冯诺依曼这个人的名字大家一定不会感到陌…

CentOS8启动过程与服务单元控制systemctl

一、启动过程 CentOS8采用了systemd&#xff0c;启动过程被大大缩短了。具体启动过程如下&#xff1a; 1、开机自检。 2、从硬盘的MBR中读取引导程序GRUB。 3、引导程序更加配置文件显示引导菜单。 4、如果选择进入Linux系统&#xff0c;此时引导程序就会加载Linux内核文件…

​TikTok正在监管机构谈判,以获得印尼支付牌照

8月4日&#xff0c;据路透社报道&#xff0c;两位消息人士透露&#xff0c;TikTok正在与印尼监管机构进行初步谈判&#xff0c;以求获得印尼央行颁发的PJP支付牌照。若获得该项资质&#xff0c;TikTok将能够更好的在本地开展电商等各项业务。 TikTok发言人于8月4日证实了这一消…

Redis压缩列表

区分一下 3.2之前 Redis中的List有两种编码格式 一个是LINKEDLIST 一个是ZIPLIST 这个ZIPLIST就是压缩列表 3.2之后来了一个QUICKLIST QUICKLIST是ZIPLIST和LINKEDLIST的结合体 也就是说Redis中没有ZIPLIST和LINKEDLIST了 然后在Redis5.0引入了LISTPACK用来替换QUiCKLIST中的…

【《深入浅出计算机网络》学习笔记】第1章 概述

内容来自b站湖科大教书匠《深入浅出计算机网络》视频和《深入浅出计算机网络》书籍 目录 1.1 信息时代的计算机网络 1.1.1 计算机网络的各类应用 1.1.2 计算机网络带来的负面问题 1.2 因特网概述 1.2.1 网络、互联网与因特网的区别与关系 1.2.1.1 网络 1.2.1.2 互联网 …

Redis学习笔记Day01-Redis入门

声明&#xff1a;本博客部分内容是从终极SpringBoot讲义摘抄的&#xff0c;文字是OCR识别出来的&#xff0c;有可能存在识别错误的可能&#xff0c;如有错误&#xff0c;请大胆指正&#xff0c;我马上修改&#xff01; 目录 1.连接命令2.key相关命令3.String命令4.List命令5.S…

Vue [Day4]

组件的三大组成部分 组件的样式冲突 scoped <style scoped></style>data 是一个函数 components/BaseButton.vue <template><div class"BaseButton"><button click"count--">-</button><span>{{ count }}</…

软件外包开发的GO语言特点

Go语言&#xff08;也称为Golang&#xff09;是由Google开发的一种编程语言。它具有许多特点&#xff0c;使其成为许多项目范围的优秀选择。Go语言适用于需要高性能、并发和简洁易读的项目&#xff0c;特别是面向网络和分布式应用的项目。今天和大家分享项目的特点及适用的项目…

【深度学习环境】安装anaconda、tensorflow、pycharm

目录 1.安装anaconda 2.安装tensorflow-gpu 3.安装pycharm 4.VNC操作 5.安装Pytorch PS: linux下常见的操作&#xff1a; 1.Linux下强制关闭程序&#xff1a; 2.导出环境 2.1.pip导出 2.2.conda导出 2.3.其他 3.windows下的环境安装 & pycharm远程配置 4.bash…

postman和jmeter的区别何在?

小伙伴们大家好呀&#xff0c;前段时间笔者做了一个小调查&#xff0c;发现软件测试行业做功能测试和接口测试的人相对比较多。在测试工作中&#xff0c;有高手&#xff0c;自然也会有小白&#xff0c;但有一点我们无法否认&#xff0c;就是每一个高手都是从小白开始的&#xf…

BS框架说明

B/S架构 1.B/S框架&#xff0c;意思是前端&#xff08;Browser 浏览器&#xff0c;小程序、app、自己写的&#xff09;和服务器端&#xff08;Server&#xff09;组成的系统的框架结构 2.B/S框架&#xff0c;也可理解为web架构&#xff0c;包含前端、后端、数据库三大组成部分…

数据可视化(七)常用图表的绘制

1. #seaborn绘制常用图表 #折线图 #replot&#xff08;x&#xff0c;y&#xff0c;kind&#xff0c;data&#xff09; #lineplot&#xff08;x&#xff0c;y&#xff0c;data&#xff09; #直方图 #displot&#xff08;data&#xff0c;rug&#xff09; #条形图 #barplot&…

测试老鸟总结,性能测试需求分析-性能必要性,一篇打通...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 性能测试需求分析…

基于react-native的简单消息确认框showModel

基于react-native的简单消息确认框showModel 效果示例图组件代码ShowModel/index.jsx使用案例device.js安装线性渐变色 效果示例图 组件代码ShowModel/index.jsx import React, {forwardRef, useImperativeHandle, useState} from react; import {View,Text,Modal,TouchableOp…

Babylon.js开发工具链大全

本文介绍Babylon 团队&#xff08;JS 和原生&#xff09;和社区共同创建的所有出色工具的摘要&#xff0c;以帮助开发人员和设计人员创建出色的 3D 体验。 推荐&#xff1a;用 NSDT设计器 快速搭建可编程3D场景。 1、Sandbox 第一个工具Sandbox可能是最简单的&#xff0c;它实…

基于RASC的keil电子时钟制作(瑞萨RA)(8)----按键修改数码管时间

基于RASC的keil电子时钟制作8_按键修改数码管时间 概述硬件准备视频教程配置按键管脚按键设置主程序timer_smg.ctimer_smg.h 概述 前几节课程已经单独驱动了数码管和RTC&#xff0c;同时已经整合成了能够用数码管显示具体时间&#xff0c;但是无法修改时间&#xff0c;这节就来…

web基础与tomcat环境部署

一. 简述静态网页和动态网页的区别。 请求响应信息&#xff0c;发给客户端进行处理&#xff0c;由浏览器进行解析&#xff0c;显示的页面称为静态页面。处理文件类型如.html、jpg、.gif、.mp4、.swf、.avi、.wmv、.flv等 请求响应信息&#xff0c;发给事务端进行处理&#xff0…

信号平滑或移动平均滤波研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

数据库与数据仓库的区别及关系

数据库与数据仓库的区别及关系 数据库数据仓库异同差异联系例子 数据库 数据库是结构化信息或数据的有序集合&#xff0c;一般以电子形式存储在计算机系统中。通常由数据库管理系统 (DBMS) 来控制。它是一个长期存储在计算机内的、有组织的、可共享的、统一管理的大量数据的集…

【导出Word】如何使用Java+Freemarker模板引擎,根据XML模板文件生成Word文档(只含文本内容的模板)

这篇文章&#xff0c;主要介绍如何使用JavaFreemarker模板引擎&#xff0c;根据XML模板文件生成Word文档。 目录 一、导出Word文档 1.1、基础知识 1.2、制作模板文件 1.3、代码实现 &#xff08;1&#xff09;引入依赖 &#xff08;2&#xff09;创建Freemarker工具类 &…