C++三大特性—继承“名字搜索与默认成员函数”

news2024/10/1 15:10:36

继承中的类的作用域

每个类定义自己的作用域,在这个作用域中定义自己的成员。当存在继承关系时,派生类的作用域嵌套在基类的作用域之中。如果一个名字在派生类的作用域中无法解析,那么编译器将继续在外层的基类中寻找该名字的定义。

继承关系如下:在这里插入图片描述
我们看下面代码:

	Child obj;
	obj.fun();

   我们通过Child类型的obj去调用fun函数,所以我们首先在Child类域中查找,如果没找到。
   因为Child继承于Teacher,Child是Teacher的派生类,所以接下来我们继续在Teacher的类域中查找fun函数,如果没找到。
   因为Teacher继承于Person,Teacher是Person的派生类,所以接下来我们继续在Person的类域中查找。一直找到最终的基类。

编译时的名字搜索

一个对象、引用、指针的静态类型决定了该对象的哪个成员是可见的,即使它的静态类型与动态类型可能不一致,但是它能使用哪个类型依然是由静态类型决定的

举个例子:

class Person
{
protected:
	string _name; // 姓名
	string _sex;  // 性别
	int _age;     // 年龄
};
class Teacher : public Person
{
public:
	int _No; // 学号
	void fun() const
	{
		cout << _No;
	}
};

我们只能通过Teacher及其派生类的对象、指针、引用去访问fun函数

	Teacher obj;              
	Teacher* obj_Tea = &obj;  //静态类型与动态类型一致
	Person* obj_Per = &obj;   //静态类型与动态类型不一致
	obj_Tea->fun();           //正确、类型为Teacher
	obj_Per->fun();           //错误、类型为Person

   虽然obj之中确实是有一个名字为fun的函数,但是这个成员对于obj_Per是不可见的。obj_Per的类型是一个Person类型,那么就意味着对于fun的的搜索是从Person开始的,显然Person类中没有fun函数,所以我们无法通过Person的对象、指针、引用去调用fun。

名字冲突与继承

  • 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)
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; // 学号
};
void Test()
{
	Student s1;
	s1.Print();
};

运行结果:在这里插入图片描述
   子类成员中有_num,父类成员中也有_num,所以正常在子类里面访问_num,会隐藏父类继承来的_num,而访问子类本身有的成员,如果要访问这个隐藏的成员,需要在前面加上Person::

  • 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
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);
};

运行结果:在这里插入图片描述
B中的fun和A中的fun不是构成重载,因为不是在同一作用域
B中的fun和A中的fun构成隐藏,成员函数满足函数名相同就构成隐藏。

如果有隐藏默认调用自身所在类的,如果需要调用父类的,就加上 父类::(A::)

这种方式叫:使用作用域运算符来使用隐藏的基类成员
作用域运算符将覆盖掉原有的查找规则,并指示编译器从指定类的作用域开始查找成员

派生类的成员将隐藏同名的基类成员
除了覆盖继承而来的虚函数之外,派生类最好不要重用其他定义在基类中的名字。

理解函数调用的解析过程对于理解C++继承至关重要:
如果我们需要调用一个函数:obj.fun();
1.我们首先要确定obj的静态类型,因为我们调用的是一个成员,所以该类型必然是类类型
2.在obj静态类型对应的类中查找fun,如果找不到,则依次在直接基类中不断查找直到到达继承链的顶端,如果找完了还找不到,编译器则会报错
3.一旦找到了fun,先进行常规的类型检查,以确定找到的fun合法
4.调用合法,编译器将根据调用的是否是虚函数而产生不同的代码

  • 如果fun是虚函数且是通过指针或引用,则编译器产生的代码将在运行时确定到底运行该虚函数的哪个版本,依据的是对象的动态类型
  • 如果不是虚函数,是通过对象(而非引用或指针)进行调用,编译器将产生一个常规函数调用

名字查找优先类型检查

   声明在内层作用域的函数并不会重载声明在外层作用域的函数、因此,定义在派生类中的函数也不会重载其基类中的成员
   如果派生类(即内层作用域)的成员与基类(即外层作用域)的某个成员同名,那么派生类将在其作用域内隐藏该基类成员,即使该派生类成员与基类成员的形参列表不一致,基类成员仍然会被隐藏。


派生类的默认成员函数

派生类的构造函数

   派生类对象中含有从基类继承过来的成员,但是派生类并不能直接初始化这些成员,派生类必须使用基类的构造函数初始化它的基类部分。

   派生类对象的基类部分与它自己的数据成员都是在构造函数的初始化阶段执行初始化操作的。派生类构造函数同样是通过构造函数初始化列表来讲实参传递给基类的构造函数。
且看下面分析:

class Person
{
public:
	Person(const string name,const string sex,int age)
		:_name(name),_sex(sex),_age(age){}
protected:
	string _name; // 姓名
	string _sex;  // 性别
	int _age;     // 年龄
};
class Teacher : public Person
{
public:
	Teacher(const string name, const string sex, int age,int No)
		:Person(name,sex,age),_No(No)
	{}
	int _No; // 学号
};

   Teacher自己的构造函数,将前三个参数传递给Person的构造函数,Person的构造函数负责初始化Teacher的基类部分,接下来初始化派生类自己定义的成员,最后运行Teacher空的函数体。
   除非我们特别指出,否则派生类对象的基类部分会像数据成员一样执行默认初始化,如果你想使用其他基类的构造函数,我们需要以类名加圆括号内的实参列表的形式来为构造函数提供初始值。这些实参将告诉编译器到底使用哪一个构造函数来初始化派生类的基类部分。

首先初始化基类部分,然后按照声明的顺序依次初始化派生类的成员

在这里插入图片描述
1、子类析构函数和父类析构函数构成隐藏关系。(由于多态关系需求,所有析构函数都会特殊处理成destructor函数名)
2、子类先析构,父类再析构。子类析构函数不需要显示调用父类析构,子类析构后会自动调用父类析构

默认成员函数规则总结:
1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
3. 派生类的operator=必须要调用基类的operator=完成基类的复制。
4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。
5. 派生类对象初始化先调用基类构造再调派生类构造
6. 派生类对象析构清理先调用派生类析构再调基类的析构
7. 因为后续一些场景析构函数需要构成重写,重写的条件之一是函数名相同(这个我们后面会讲解)。那么编译器会对析构函数名进行特殊处理,处理成destrutor(),所以父类析构函数不加virtual的情况下,子类析构函数和父类析构函数构成隐藏关系


派生类的声明

  派生类的声明与其他类差别不大,声明包含类名但是不包含它的派生列表

class Student : Person; //错误:派生列表不能出现在声明
class Student;          //正确声明方式

一条声明语句的目的是令程序知晓某个名字的存在,以及该名字表示一个什么样的实体。派生列表以及定义有关的细节必须与类的主体一起出现。

用作基类的类

如果我们想将某个类用作基类,那么这个类必须已经定义而非只声明

一个类是基类,同时它也可以是一个派生类,但是一个类不能派生它自己。

class Person{...};
class Teacher : private Person{...};
class Child : public Teacher{...};

  在这个继承关系中,Person是Teacher的直接基类,同时也是Child的间接基类。
  每个类都会继承直接基类的所有成员。最终的派生类将包含它的直接基类的子对象以及每个间接基类的子对象。

继承与友元

   就像友元关系不能传递一样,友元关系同样不能继承。基类的友元在访问派生类的成员时不具有特殊性,类似的,派生类的友元也不能随意访问基类的成员

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

Display是基类Person的友元,cout << s._stuNum << endl;这条语句想要访问Students的受保护成员_stuNum,显然是不可以的。基类友元不能访问派生类私有和保护成员。
不能继承友元关系,每个类负责控制各自成员的访问权限


继承与静态成员

  如果基类定义了一个静态成员,则在整个继承体系中只存在该成员的唯一定义,不论从基类中派生多少个派生类出来,对于每个静态成员来说都只存在唯一实例。

class Person
{
public:
	static string _age;
};

静态成员遵循通用的访问规则,如果基类中成员是private,那么派生类无权访问它。假设某静态成员是可访问的,那么我们既可以通过基类也能通过派生类使用它。


如有错误或者不清楚的地方欢迎私信或者评论指出🚀🚀

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

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

相关文章

next.js博客搭建_初始化next项目(第一步)

文章目录 ⭐前言⭐next初始化TypeScript 开发项目安装react的ui框架&#xff08;tDesign&#xff09;设计布局 ⭐结束 ⭐前言 大家好&#xff0c;我是yma16&#xff0c;本期给大家分享next项目搭建博客的开始。 背景 因为我的博客网站https://yongma16.xyz是基于vue2搭建的&am…

Centos7快速安装Kibana并连接ES使用

Elasticsearch 提供了一个名为 Kibana 的官方可视化界面。Kibana 是一个开源的数据可视化和管理工具&#xff0c;用于 Elasticsearch。它提供了丰富的功能&#xff0c;如仪表板、图表、地图等&#xff0c;帮助您更好地理解、搜索和可视化存储在 Elasticsearch 中的数据。 在 C…

营收、利润增速第一!海尔智家为何领跑?

“企业只有保持领先的能力&#xff0c;才有可能取得经济成果。” 管理学大师德鲁克曾如此强调。所谓“领先”&#xff0c;就是独一无二的、有价值的东西。利润&#xff0c;是企业在某个领域取得领先优势后&#xff0c;必然获得的回报。 这种“领先优势”&#xff0c;在各行业…

gpt4入口在哪里-怎么使用chatGPT4

Chat GPT 4.0 PLUS详细介绍 Chat GPT 4.0 PLUS是一款基于人工智能技术的自然语言处理模型&#xff0c;它是目前最先进的Chat GPT系列中的一员。Chat GPT 4.0 PLUS具有极高的精度和稳定性&#xff0c;可以帮助用户实现高品质、高效率的自然语言处理体验。下面详细介绍Chat GPT 4…

Python小姿势 - # Python爬虫技术

Python爬虫技术 许多人认为爬虫技术只能用于网页内容抓取&#xff0c;其实爬虫技术还可以用于更多的场景&#xff0c;比如数据挖掘、信息处理等。在这里&#xff0c;我们就来学习如何使用Python来编写爬虫。 首先&#xff0c;我们需要准备一个Python爬虫的开发环境。Python是一…

嵌入式Linux底层系统开发 +系统移植+内核文件系统(基础)

嵌入式Linux系统移植要点&#xff1a; 搭建交叉编译开发环境bootloader的选择和移植kernel的配置、编译、移植和调试根文件系统的制作 前两个要点通常芯片厂家提供。后边两个要点是公司的工作重点。 学习方法&#xff1a;先整体后局部&#xff0c;层层推进 如何编译—>如何…

idea修改 项目代码,浏览器页面不生效 解决方案

使用快捷键ctrl shift delete&#xff0c;清理浏览器缓存 1、问题描述 idea修改前端项目代码&#xff0c;运行谷歌浏览器不起作用。 我也试过 rebuild project, 重启idea&#xff0c;等方法都不管用。 再次运行谷歌浏览器&#xff0c;还是没有变化。 2、尝试了以下方法&am…

ElasticSearch学习随笔之分词算法

ElasticSearch 1、ElasticSearch学习随笔之基础介绍 2、ElasticSearch学习随笔之简单操作 3、ElasticSearch学习随笔之java api 操作 4、ElasticSearch学习随笔之SpringBoot Starter 操作 5、ElasticSearch学习随笔之嵌套操作 6、ElasticSearch学习随笔之分词算法 ElasticSea…

JUC之Java内置锁的核心原理

文章目录 JUC之Java内置锁的核心原理Java对象结构对象头对象体对齐字节 Mark Word的结构信息64位Mark Word的构成 偏向锁偏向锁的设置偏向锁的重偏向偏向锁的撤销偏向锁的膨胀 轻量级锁执行过程轻量级锁的分类普通自旋锁自适应自旋锁 重量级锁偏向锁、轻量级锁与重量级锁的对比…

探秘C语言经典题目:如何求解整数二进制中1的个数

本篇博客会讲解一道经典的题目&#xff1a;求一个整数二进制中1的个数。阅读本篇博客前&#xff0c;需要你对C语言如何进行二进制位操作有一定的了解&#xff0c;如果还不太了解的话&#xff0c;可以阅读一下我的这篇博客。 我们假设有一个int类型的整数n&#xff0c;我们知道…

12. Transformer(上)

P32 Transformer&#xff08;上&#xff09; 视频链接 P32 Transformer&#xff08;上&#xff09; Seq2seq应用: Seq2seq结构:

武忠祥老师每日一题||定积分基础训练(二)

仍是上一节中提到的基本思想 武忠祥老师每日一题||定积分基础训练&#xff08;一&#xff09; 在这个题中&#xff0c;M和N可以利用奇偶性判断。 如下&#xff1a; 从上可知&#xff0c; M ∫ − π 2 π 2 1 d x M\int_{-\frac{\pi}{2}}^{\frac{\pi}{2}}1\,{\rm d}x M∫−…

The 1st Universal Cup Stage 13: Iberia, Apr 22-23, 2023 题解

D. XOR Determinant You are given two arrays b and c of length n, consisting of non-negative integers. Construct n n matrix A as Aij bi ⊕ cj . Find the determinant of A modulo 998 244 353 考虑 A i j ∑ k b i , k c j , k p A_{ij}\sum_k b_{i,k}{c_{j,k}…

基于GWO灰狼优化算法的城市路径优化问题GWO-TSP(MATLAB程序)

资源地址&#xff1a; 基于GWO灰狼优化算法的城市路径优化问题GWO-TSP(MATLAB程序&#xff09;资源-CSDN文库 主要内容&#xff1a; 主要采用灰狼优化算法对城市间的路径进行规划。城市分布图如图所示。 部分代码&#xff1a; % 产生问题模型 model CreateModel(Oliver30.…

p70 内网安全-域横向内网漫游 Socks 代理隧道技术(NPS、FRP、CFS 三层内网漫游)

数据来源 本文仅用于信息安全学习&#xff0c;请遵守相关法律法规&#xff0c;严禁用于非法途径。若观众因此作出任何危害网络安全的行为&#xff0c;后果自负&#xff0c;与本人无关。 ​ 必要基础知识点&#xff1a; 内外网简单知识内网 1 和内网 2 通信问题正向反向协议通…

linux用户管理指令

这里写自定义目录标题 一 增加新用户及密码二 切换用户三 userdel 删除用户四 查看用户登录信息五 让普通用户成为管理员1. 修改环境配置文件2.设置用户和密码 六 查看创建哪些用户 一 增加新用户及密码 useradd:加用户名 passwd&#xff1a;加用户密码 [rootlocalhost ~]# u…

HBASE整理

HBASE整理 一、HBASE由来 思考: HDFS主要适用于什么场景呢? 具有高的吞吐量 适合于批量数据的处理操作思考: 如果想在HDFS上, 直接读取HDFS上某一个文件中某一行数据, 请问是否可以办到呢? 或者说, 我们想直接修改HDFS上某一个文件中某一行数据,请问是否可以办到呢?HDFS并…

【Python】芜湖市空气质量指数可视化(散点图、分类散点图、单变量分布图、线性回归拟合图、相关性热力图)

【Python】芜湖市空气质量指数可视化 本文仅供学习参考&#xff0c;如有错误&#xff0c;还请指正&#xff01; 一、简介 空气质量指数&#xff08;Air Quality Index&#xff0c;AQI &#xff09;简而言之就是能够对空气质量进行定量描述的数据。空气质量(Air Quality )的好坏…

《SQLi-Labs》02. Less 6~10

sqli Less-6知识点题解 Less-7题解 Less-8题解 Less-9知识点题解 Less-10题解 sqli。开启新坑。 Less-6 知识点 布尔盲注。与 Less-5 基本相同。这里只简略写大致步骤。 length() 函数&#xff1a;返回字符串所占的字节数。ascii() 函数&#xff1a;返回字符串最左字符的ASC…

OpenGL开发必过的坎------开发环境的准备(Windows10)

前言 图形编程一直以来都是计算机科学中最具挑战性的主题之一。随着限制VR技术的兴起&#xff0c;越来越多的公司开始涉足VR领域。目前来看使用最多的是Unity3d来开发&#xff0c;但是像浏览器&#xff0c;将2D应用3D化&#xff08;把2D的应用界面投到一个3D的场景中&#xff…