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

news2024/11/14 0:42:49

文章目录

  • 继承
    • 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++继承的部分介绍了😉
如有错误❌望指正,最后祝大家学习进步✊天天开心✨🎉

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

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

相关文章

深入理解Jdk5引入的Java泛型:类型安全与灵活性并存

深入理解Jdk5引入的Java泛型&#xff1a;类型安全与灵活性并存 ​ 在Java的中&#xff0c;有一个强大的工具&#xff0c;它可以让你在编写代码时既保持类型安全&#xff0c;又享受灵活性。**这个工具就是——泛型&#xff08;Generics&#xff09;。**本文将引导你深入了解Java…

小白到运维工程师自学之路 第七十集 (Kubernetes集群部署)

一、概述 Kubernetes&#xff08;简称K8S&#xff09;是一个开源的容器编排和管理平台&#xff0c;是由Google发起并捐赠给Cloud Native Computing Foundation&#xff08;CNCF&#xff09;管理的项目。它的目标是简化容器化应用的部署、扩展、管理和自动化操作。 以下是Kube…

【D3S】集成smart-doc并同步配置到Torna

目录 一、引言二、maven插件三、smart-doc.json配置四、smart-doc-maven-plugin相关命令五、推送文档到Torna六、通过Maven Profile简化构建 一、引言 D3S&#xff08;DDD with SpringBoot&#xff09;为本作者使用DDD过程中开发的框架&#xff0c;目前已可公开查看源码&#…

leetcode26-删除有序数组中的重复项

双指针—快慢指针 慢指针 slow 走在后面&#xff0c;快指针 fast 走在前面探路&#xff0c;找到一个不重复的元素的时候就让slow前进一步并赋值给它。 流程&#xff1a; 代码 class Solution { public:int removeDuplicates(vector<int>& nums) {int slow 0, fas…

【2012年专利】基于中继节点的互联网通信系统和通信路径选择方法

基于中继节点的互联网通信系统和通信路径选择方法 CN102594703A地址基于中继节点的互联网通信系统和通信路径选择方法 ,解决:服务器间直接传输时丢包率高及延时长的缺点包括:服务器之间转发数据包的中继节点计算服务器间最优传输路径的选路决策节点, 本发明涉及一种晶于中继…

【Spring专题】Spring之底层架构核心概念解析

目录 前言前置知识课程内容一、BeanDefinition&#xff1a;图纸二、BeanDefinitionReader&#xff1a;图纸读取器——Spring工厂基础设施之一2.1 AnnotatedBeanDefinitionReader2.2 XmlBeanDefinitionReader2.3 ClassPathBeanDefinitionScanner 三、BeanFactory&#xff1a;生产…

银行电子密码器也远程管理吗?操作步骤如下

企业需要在不同办公地点管理多个资金账户&#xff0c;能不能远程点按读取银行电子密码器呢&#xff1f; 三个字&#xff0c;很简单&#xff01; 可以用密码点按器&#xff01; 有了它就可以自动输入密码和读取验证码&#xff01; 第一步 用双面胶把密码器固定在点按器上&…

【UE4 RTS】04-Camera Pan

前言 本篇实现了CameraPawn的旋转功能。 效果 步骤 1. 打开项目设置&#xff0c;添加两个操作映射 2. 打开玩家控制器“RTS_PlayerController_BP”&#xff0c;新建一个浮点型变量&#xff0c;命名为“PanSpeed” 在事件图表中添加如下节点 此时运行游戏可以发现当鼠标移动…

【学习日记】【FreeRTOS】调度器函数实现详解

写在前面 本文主要是对于 FreeRTOS 中调度器函数实现的详细解释&#xff0c;代码大部分参考了野火 FreeRTOS 教程配套源码&#xff0c;作了一小部分修改。 一、MSP 和 PSP Cortex-M有两种栈空间&#xff0c;主堆栈和进程堆栈。 MSP 用于系统级别和中断处理的堆栈 MSP 用于保…

一文看懂Apipost接口自动化使用方法

随着项目研发进程的不断推进&#xff0c;软件功能不断增多&#xff0c;对于软件测试的要求也越来越高。为了提高测试效率和减少测试成本&#xff0c;许多软件测试团队借助于自动化测试工具来优化测试流程。Apipost也提供了自动化测试工具&#xff0c;在本文中&#xff0c;我们将…

Android Studio System.out.println()中文乱码

第一步&#xff1a; 打开studio64.exe.vmoptions加入-Dfile.encodingUTF-8 第二步&#xff1a; File-Settings-Editor-File Encodings 把所有的编码格式改为UTF-8 尝试跑一下代码&#xff0c;如果还不行&#xff0c;重启IDE 再试试。

利用DNS隧道构建隐蔽CC信道

背景介绍 无论是高级持续性威胁(APT&#xff09;、僵尸网络(Botnet)&#xff0c;还是勒索软件、后门等&#xff0c;命令与控制信道(C &C)都是其重要组成部分&#xff0c;尤其是APT和僵尸网络中的C&C信道决定了其威胁程度。学术界和工业界就C&C方面的研究已逐渐深入…

vue3:新特性

一、react和vue的主要区别 &#xff08;1&#xff09;数据更新上&#xff1a; 1、 react 采用 fiber架构 &#xff0c;使用 链表 表示 DOM 结构可以在 diff 时随时中断和继续&#xff0c;利用requestIdleCallback 在空闲时 diff &#xff0c;防止数据量大 diff 时间长导致卡顿…

Python 之禅

Python 社区的理念都包含在 Tim Peters 撰写的 “Python 之禅” 中 在 Windows 平台的 cmd 命令中打开 python&#xff0c;输入 import this&#xff0c;就能看到 Python 之禅: 翻译&#xff1a; Tim Peters 的 python 之禅Beautiful is better than ugly. # 优美胜于丑陋&am…

生成树协议

文章目录 STP冗余交换网络为什么存在广播风暴&#xff1f;广播的危害&#xff1f;交换环路的危害&#xff1f; 工作机制BPDU什么是最好的bpduBPDU触发机制 STP选举步骤配置协议分析字段分析开销模式端口状态 故障类型根桥故障直连故障间接故障 &#xff08;链路中间可能有HUB&a…

vuejs 设计与实现 - 渲染器的设计

渲染器与响应式系统的结合 本节&#xff0c;我们暂时将渲染器限定在 DOM 平台。既然渲染器用来渲染真实 DOM 元素&#xff0c;那么严格来说&#xff0c;下面的函数就是一个合格的渲染器: // 渲染器&#xff1a; function renderer(domString, container) {container.innerHTM…

中级课程-SSRF(CSRF进阶)

文章目录 成因危害挖掘 成因 危害 挖掘

JUC并发编程之线程锁(一)

目录 1.ReentrantLock(互斥锁) 2.ReentRantReaderWriterLock&#xff08;互斥读写锁&#xff09; 3.StampedLock&#xff08;无障碍锁&#xff09; 4.Condition&#xff08;自定义锁&#xff09; 5.LockSupport 问题引出&#xff1a; 由于传统的线程控制需要用到同步机制Sy…

LeetCode--HOT100题(23)

目录 题目描述&#xff1a;206. 反转链表&#xff08;简单&#xff09;题目接口解题思路代码 PS: 题目描述&#xff1a;206. 反转链表&#xff08;简单&#xff09; 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 LeetCode做题链接&…

剑指offer56-II.数组中数字出现的次数II

第一种方法非常简单&#xff0c;就是用一个HashMap&#xff0c;key是数组中元素的值&#xff0c;value是出现的次数&#xff0c;所以遍历一遍数组&#xff0c;如果map中没有这个元素就把它put进去value设为1&#xff0c;否则value加1&#xff0c;然后遍历一遍map&#xff0c;如…