C++从入门到起飞之——继承上篇 全方位剖析!

news2024/12/24 9:03:44

🌈个人主页:秋风起,再归来~
🔥系列专栏:C++从入门到起飞          
🔖克心守己,律己则安

目录

1、继承的概念

 2、继承定义

2.1 定义格式

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

3、继承类模板

4、 基类和派⽣类间的转换 

5、继承中的作⽤域

5.1 隐藏规则:

​编辑 5.2 考察继承作⽤域相关选择题

6、完结散花


1、继承的概念

继承(inheritance)机制是⾯向对象程序设计使代码可以复⽤的最重要的⼿段,它允许我们在保持原有 类特性的基础上进⾏扩展,增加⽅法(成员函数)和属性(成员变量),这样产⽣新的类,称派⽣类。继承 呈现了⾯向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的函数层次的 复⽤,继承是类设计层次的复⽤。

下⾯我们看到没有继承之前我们设计了两个类Student和Teacher,Student和Teacher都有姓名/地址/ 电话/年龄等成员变量,都有identity⾝份认证的成员函数,设计到两个类⾥⾯就是冗余的。当然他们 也有⼀些不同的成员变量和函数,⽐如⽼师独有成员变量是职称,学⽣的独有成员变量是学号;学⽣ 的独有成员函数是学习,⽼师的独有成员函数是授课。

class Student
{
public:
 // 进⼊校园/图书馆/实验室刷⼆维码等⾝份认证 
 void identity()
 {
 // ...
 }
 // 学习 
 void study()
 {
 // ...
 }
protected:
 string _name = "peter"; // 姓名 
 string _address; // 地址 
 string _tel; // 电话 
 int _age = 18; // 年龄 
 int _stuid; // 学号 
};
class Teacher
{
public:
// 进⼊校园/图书馆/实验室刷⼆维码等⾝份认证 
 void identity()
 {
 // ...
 }
 // 授课 
 void teaching()
 {
 //...
 }
protected:
 string _name = "张三"; // 姓名 
 int _age = 18; // 年龄 
 string _address; // 地址 
 string _tel; // 电话 
 string _title; // 职称 
};
int main()
{
 return 0;
}

下⾯我们公共的成员都放到Person类中,Student和teacher都继承Person,就可以复⽤这些成员,就 不需要重复定义了,省去了很多⿇烦。

class Person
{
public:
 // 进⼊校园/图书馆/实验室刷⼆维码等⾝份认证 
 void identity()
 {
 cout << "void identity()" <<_name<< endl;
 }
protected:
 string _name = "张三"; // 姓名 
 string _address; // 地址 
 string _tel; // 电话 
 int _age = 18; // 年龄 
};
class Student : public Person
{
public:
 // 学习 
 void study()
 {
 // ...
 }
protected:
 int _stuid; // 学号 
};
class Teacher : public Person
{
public:
 // 授课 
 void teaching()
 {
 //...
 }
protected:
 string title; // 职称 
};
int main()
{
 Student s;
 Teacher t;
 s.identity();
 t.identity();
 return 0;
}

 2、继承定义

2.1 定义格式

下⾯我们看到Person是基类,也称作⽗类。Student是派⽣类,也称作⼦类。(因为翻译的原因,所以 既叫基类/派⽣类,也叫⽗类/⼦类)

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

1. 基类private成员在派⽣类中⽆论以什么⽅式继承都是不可⻅的。这⾥的不可⻅是指基类的私有成员 还是被继承到了派⽣类对象中,但是语法上限制派⽣类对象不管在类⾥⾯还是类外⾯都不能去访问 它。

虽然我们不能直接使用父类的私有成员,但是我们可以通过父类的成员函数间接使用!

class Person
{
public:
	void p_print()
	{
		cout << _name << endl;
		cout << _age << endl;
	}
protected:
	string _name="张三"; // 姓名 
private:
	int _age=18; // 年龄 
};


class Student : public Person
{
public:
	//可以直接在子类里面访问父类的保护成员
	void s_print()
	{
		cout << _name << endl;
	}
protected:
	int _stunum; // 学号 
};
int main()
{
	Student s1;
	s1.p_print();
	return 0;
}

 

父类的公有成员函数当然也被继承下来了,我们可以通过调用父类的成员函数来访问父类的私有成员!

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

// 实例演⽰三种继承关系下基类成员的各类型成员访问关系的变化  
class Person
{
public:
	void p_print()
	{
		cout << _name << endl;
	}
protected:
	string _name; // 姓名 
private:
	int _age; // 年龄 
};


class Student : public Person
{
public:
	//可以直接在子类里面访问父类的保护成员
	void s_print()
	{
		cout << _name << endl;
	}
protected:
	int _stunum; // 学号 
};

3. 实际上⾯的表格我们进⾏⼀下总结会发现,基类的私有成员在派⽣类都是不可⻅。基类的其他成员 在派⽣类的访问⽅式==Min(成员在基类的访问限定符,继承⽅式)public >protected> private。

4. 使⽤关键字class时默认的继承⽅式是private,使⽤struct时默认的继承⽅式是public,不过最好显 ⽰的写出继承⽅式

//默认是private继承
class Student : Person
{
public:
	//可以直接在子类里面访问父类的保护成员
	void s_print()
	{
		cout << _name << endl;
	}
protected:
	int _stunum; // 学号 
};

//默认是public继承
struct Student : Person
{
public:
	//可以直接在子类里面访问父类的保护成员
	void s_print()
	{
		cout << _name << endl;
	}
protected:
	int _stunum; // 学号 
};

5. 在实际运⽤中⼀般使⽤都是public继承,⼏乎很少使⽤protetced/private继承,也不提倡使⽤ protetced/private继承,因为protetced/private继承下来的成员都只能在派⽣类的类⾥⾯使⽤,实 际中扩展维护性不强。

3、继承类模板

当我们继承的父类是类模版时,我们在子类里面需要使用父类的成员函数时一定要指定类域访问,不然编译器就会找不到这个函数。这是按需实例化的缘故,我们在实例化stack对象时,我们只使用了stack和父类的构造函数,所以编译器就只实例化了它们的构造函数。如果我们用stack对象去调用其他接口时,编译器才会按需实例化它们!如果我们不在子类指定类域,编译器就找不到父类的成员函数,然后报错。

namespace my_stack
{
	//template<class T>
	//class vector
	//{};
	// stack和vector的关系,既符合is-a,也符合has-a 
	template<class T>
	class stack : public std::vector<T>
	{
	public:
		void push(const T& x)
		{
			// 基类是类模板时,需要指定⼀下类域, 
			// 否则编译报错:error C3861: “push_back”: 找不到标识符 
			// 因为stack<int>实例化时,也实例化vector<int>了 
			// 但是模版是按需实例化,push_back等成员函数未实例化,所以找不到 
			vector<T>::push_back(x);
			//push_back(x);
		}
		void pop()
		{
			vector<T>::pop_back();
		}
		const T& top()
		{
			return vector<T>::back();
		}
		bool empty()
		{
			return vector<T>::empty();
		}
	};
}
int main()
{
	my_stack::stack<int> st;
	st.push(1);
	st.push(2);
	st.push(3);
	while (!st.empty())
	{
		cout << st.top() << " ";
		st.pop();
	}
	return 0;
}

4、 基类和派⽣类间的转换 

• public继承的派⽣类对象可以赋值基类的指针/基类的引⽤。这⾥有个形象的说法叫切⽚或者切 割。寓意把派⽣类中基类那部分切出来,基类指针或引⽤指向的是派⽣类中切出来的基类那部分。

class Person
{
//protected:
	virtual void func()
	{}
public:
	string _name; // 姓名
	string _sex; // 性别
	int _age; // 年龄
};

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

int main()
{
	Student sobj;

	// 1.子类对象可以赋值给父类对象/指针/引用
	Person pobj = sobj;
	Person* pp = &sobj;
	Person& rp = sobj;
	rp._name = "张三";
    return 0;
}

需要注意的一点是,子类赋值给父类并不是走的隐式类型转换,因为如果是的话,子类可以赋值给父类的引用就说不通了!在这个过程中,编译器回先用子类对象拷贝构造一个临时对象,这个临时对象具有常性,给普通的引用,权限被放大的了!

事实上!派⽣类对象可以赋值给基类的对象是通过调⽤后⾯会讲解的基类的拷⻉构造完成的  

基类对象不能赋值给派⽣类对象。(很好理解,子类的对象有的成员,父类对象不一定有!)
基类的指针或者引⽤可以通过强制类型转换赋值给派⽣类的指针或者引⽤。但是必须是基类的指针 是指向派⽣类对象时才是安全的。这⾥基类如果是多态类型,可以使用RTTI(RunTimeType Information)的dynamic_cast来进⾏识别后进⾏安全转换。(这个我们后⾯单独专⻔讲解,这⾥先提⼀下)

5、继承中的作⽤域

5.1 隐藏规则:

1. 在继承体系中基类和派⽣类都有独⽴的作⽤域。

2. 派⽣类和基类中有同名成员,派⽣类成员将屏蔽基类对同名成员的直接访问,这种情况叫隐藏。 (在派⽣类成员函数中,可以使⽤基类::基类成员显⽰访问)

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

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

// Student的_num和Person的_num构成隐藏关系,可以看出这样代码虽然能跑,但是⾮常容易混淆 
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; // 学号 
};
int main()
{
 Student s1;
 s1.Print();
 
 return 0;
};

 5.2 考察继承作⽤域相关选择题

class A
{
public:
	void fun()
	{
		cout << "func()" << endl;
	}
};
class B : public A
{
public:
	void fun(int i)
	{
		cout << "func(int i)" << i << endl;
	}
};
int main()
{
	B b;
	b.fun(10);
	b.fun();

	return 0;
};

1、A和B类中的两个func构成什么关系()

A.重载  B. 隐藏  C.没关系

解析:大多数人会被选项中的重载给迷惑,因为这两个同名函数参数不同,不就构成重载吗?但是,我们不要忘了构成函数重载的前提条件(在同一作用域下!

而我们都知道在类里面定义的成员变量受访问限定符类域的限制!A类和B类中的同名函数所在类域不同,因此并不构成重载,根据我们上面学习的隐藏规则,我们可以知道它们构成隐藏!

2、上⾯程序的编译运⾏结果是什么()

A.编译报错  B.运⾏报错  C.正常运⾏

解析:第一个fun调用我们都知道肯定是调用到子类的fun,那第二个呢?是不是调用了被继承下来的父类的fun呢?答案是否定的!因为这两个函数构成了隐藏,被继承下来的父类函数必须突破域(指定类域)才可以访问!

答案是编译报错!

突破类域访问! 

 

6、完结散花

好了,这期的分享到这里就结束了~

如果这篇博客对你有帮助的话,可以用你们的小手指点一个免费的赞并收藏起来哟~

如果期待博主下期内容的话,可以点点关注,避免找不到我了呢~

我们下期不见不散~~

​​​​

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

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

相关文章

linux网络编程——UDP编程

写在前边 本文是B站up主韦东山的4_8-3.UDP编程示例_哔哩哔哩_bilibili视频的笔记&#xff0c;其中有些部分博主也没有理解&#xff0c;希望各位辩证的看。 UDP协议简介 UDP 是一个简单的面向数据报的运输层协议&#xff0c;在网络中用于处理数据包&#xff0c;是一种无连接的…

操作系统 ---- 处理机调度

一、处理机调度学习路线 二、调度要研究的问题&#xff1f; 当有一堆任务要处理&#xff0c;但由于资源有限&#xff0c;这些事情没法同时处理。这就需要确定某种规则来决定处理这些任务的顺序&#xff0c;这就是“调度”研究的问题。 三、调度的三个层次 3.1 高级调度&…

深入解读Docker核心原理:Namespace资源隔离机制详解

在容器技术中&#xff0c;资源隔离 是容器化能够实现轻量级虚拟化的关键技术之一。通过资源隔离&#xff0c;容器可以拥有自己的独立环境&#xff0c;确保容器之间互不干扰&#xff0c;从而实现应用的安全和稳定。Docker作为主流的容器平台&#xff0c;其核心的资源隔离机制依赖…

LabVIEW软件授权与分发要求

在LabVIEW开发中&#xff0c;将软件打包成安装程序并销售给其他公司&#xff08;例如对知识产权有严格要求的国外公司&#xff09;时&#xff0c;涉及授权和许可的多个关键环节。NI对LabVIEW的开发、分发、安装和使用都有明确的授权要求&#xff0c;以确保知识产权的合法性和软…

CentOS 7 最小化安装后如何安装图形化桌面

CentOS 7 最小化安装后如何安装图形化桌面 一、准备工作1. 登录系统2. 配置网络 二、安装图形化桌面1. 安装 X Window System2. 安装 GNOME 桌面 三、配置默认启动模式1. 查看当前启动模式2. 修改默认启动模式 四、重启系统并验证 &#x1f496;The Begin&#x1f496;点点关注…

基于微信小程序的高校实验室管理系统的设计与实现

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 基于微信小程序JavaSpringBootVueMySQL的高…

FreeRTOS学习笔记(十一)内存管理

文章目录 前言一、内存管理1.1 内存管理的引入1.2 内存碎片 二、内存分配的方法2.1 heap_12.1.1 实现原理2.1.2 源码解析 2.2 heap_2 内存分配方法2.2.1 实现原理2.2.2 源码解析 2.3 heap_3 内存分配方法2.4 heap_4 内存分配方法2.4.1 实现原理2.4.2 源码解析 2.5 heap_5 内存分…

【论文解读系列】DPD-BiReconstructor的神经网络架构

原标题&#xff1a;Semisupervised Neural Proto-Language Reconstruction 论文地址&#xff1a;https://arxiv.org/pdf/2406.05930 现有实现祖先语言&#xff08;原语言&#xff09;比较重建的工作通常需要完全监督。然而&#xff0c;如果历史重建模型只能用少量标记数据进行训…

二、栈和队列-算法总结

文章目录 二、栈和队列2.1 基本应用2.1.1 逆波兰表达式求值2.1.2 有效的括号 2.2 单调栈2.2.1 柱状图中最大的矩形 二、栈和队列 2.1 基本应用 2.1.1 逆波兰表达式求值 150. 逆波兰表达式求值 class Solution {/**思路分析&#xff1a;遇到数则压栈&#xff0c;遇到运算符…

每日一练12:杨辉三角(含链接)

1.链接 杨辉三角_牛客题霸_牛客网 2.题目 3.代码 #include <iostream> #include<vector> using namespace std; vector<int> arr(35); int main() {int n;cin>>n;for(int i1;i<n;i){if(i1) {printf("%5d",1);arr[1]1;cout<<end…

电商API接口安全:构建稳固的数字防线

电子商务的蓬勃发展带来了前所未有的便利&#xff0c;同时也带来了新的安全挑战。API接口作为电商系统的核心组件&#xff0c;其安全性直接关系到企业的数据安全和业务连续性。因此&#xff0c;评估和加固电商API接口的安全性变得尤为重要。 电商API接口安全的重要性 电商API接…

【Redis】Redis 典型应用 - 分布式锁原理与实现

目录 Redis 典型应⽤ - 分布式锁什么是分布式锁分布式锁的基础实现引⼊过期时间引⼊校验 id引⼊ lua引⼊ watch dog (看⻔狗)引⼊ Redlock 算法其他功能 Redis 典型应⽤ - 分布式锁 什么是分布式锁 在⼀个分布式的系统中&#xff0c; 也会涉及到多个节点访问同⼀个公共资源的…

YOLOv5改进:CA注意力机制【注意力系列篇】(附详细的修改步骤,以及代码)

如果实验环境尚未搭建成功&#xff0c;可以参考这篇文章 ->【YOLOv5超详细环境搭建以及模型训练&#xff08;GPU版本&#xff09;】 文章链接为&#xff1a;http://t.csdnimg.cn/Ke0bb ---------------------------------------------------------------------------​ 1…

09-排序1 排序(C)

这一节&#xff0c;测试各类排序算法的运行速度&#xff08;没有基数排序&#xff08;桶&#xff09; 其实在实际学习中&#xff0c;还是有意义的 给定 n 个&#xff08;长整型范围内的&#xff09;整数&#xff0c;要求输出从小到大排序后的结果。 本题旨在测试各种不同的排序…

Unity Addressables 使用说明(三)构建内容(Build Content)

Build Content 【概述】Build Content 内容构建会处理 Addressables 组&#xff0c;生成内容目录&#xff08;content catalog&#xff09;、运行时设置以及包含你的资源的 AssetBundles。Addressables 使用这些文件在运行时加载内容。 你可以配置 Addressables 系统将 Addr…

重磅!OpenAI正式发布博士水平的推理模型o1!附详细说明

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

【android10】【binder】【2.servicemanager启动——全源码分析】

系列文章目录 可跳转到下面链接查看下表所有内容https://blog.csdn.net/handsomethefirst/article/details/138226266?spm1001.2014.3001.5501文章浏览阅读2次。系列文章大全https://blog.csdn.net/handsomethefirst/article/details/138226266?spm1001.2014.3001.5501 目录 …

登山第九梯:稀疏点云实例分割——又快又准

文章&#xff1a;Fast Range Image-Based Segmentation of Sparse 3D Laser Scans for Online Operation 代码&#xff1a;https://github.com/PRBonn/depth_clustering 1&#xff09;摘要 从 3D 距离数据中分割对象是移动机器人领域的一个重要主题。在动态环境中导航的机器人需…

C51单片机-单按键输入识别,键盘消抖

【实验目的】 独立按键的识别方法、键盘消抖等。 【实验现象】 每按一次独立键盘的S2键&#xff0c;与P1口相连的八个发光二极管中点亮的一个往下移动一位。 【实验说明】 关于按键去抖动的解释&#xff0c;我们在手动按键的时候&#xff0c;由于机械抖动或是其它一些非人为的因…

NR PDSCH/PUSCH支持的maxMIMO layers

这里不考虑UE支持的具体MIMO能力&#xff0c;仅仅讨论协议上定的maxMIMO layers。 PDSCH 根据上面38.331中的结构&#xff0c;PDSCH max MIMO layers 为8 layers&#xff0c;进行8 layers传输时 要enable two codewords&#xff0c;因为 one codeword只能支持4 layers传输&…