类和对象plus版

news2024/11/26 15:17:14

一.类的定义

1.1类定义的格式

d94db1db45d84f4aa564a6719f0ce4d8.png

 图中class为关键字,Stack为类的名字,用{}框住类的主体,类定义完后;不能省略。

为了区分成员变量,一般习惯在成员变量前面或后面加一个特殊标识,_或者m_

3473d65bf0994c5a948296d7f5156d86.png

 1.2访问限定符

c++采用一种封装的方式,用类和对象的属性与方法结合在一起,让对象更加完善,通过访问权限选择性的将其接口提供给外部使用。

public修饰的成员在类外可以直接访问使用,private和protected修饰的成员在类外不能直接使用。

ea6021bb215447429ca2f986eaed6863.png

 

1.3类域

类定义了一个新的作用域,类的所有成员都在作用域中,在类体外定义成员时要用::操作符来指明成员。

二.实例化

类就像一个模型图纸一样,实例化之后可以实现许多的功能,但是在实例化之前,他是没有任何功能的,并且也不会占用实际的空间,只有当实例化出对象后,才能进行物理存储。

5832652d740848048a8e40464fc28fd0.png

95a5040b824b487f88b7bab29454b931.png 

 2.1对象大小

类实例化出的没一个成员都有独立的数据空间,那么类中的函数是否能进行储存呢?首先函数在编译之后是一段指令,对象无法进行存储。所以函数会以指针地址的方式,通过汇编指令call地址,找到函数的地址进行使用。

打个比方,类里面的成员变量每实例化一份就会开辟一份的空间,而类成员函数则会以一份公共的地址,放在一个代码公共区内。

99ddb0fa37904b74ad4039c7753ebea5.png

 三.this指针

在一个类中,若有多份成员函数,c++是如何找到访问对象的呢?

这里存在一个this指针,this指针在编译器编译后,会默认出现在第一个位置,增加一个当前类型的指针,类的成员访问成员变量,本质都是通过this指针来使用的。c++规定不能在形参和实参位置写this指针(编译器编译时会处理),但可以在函数体内使用this指针。

我们来看一串代码

5e1c6d8ecb894e7e95259e8aa50aaa7d.png

 它的编译结果是什么?

答案是正确通过!

p为空指针去访问类中的print函数,虽然是空指针,但是我们并没有拿去访问实体,所以不会出错。

d2a64ee7ea344c6da4c5561886083fe5.png

 若是这样去访问实体a所以就会运行崩溃

3.2this指针存在哪个区域内

1586400fb6154637b0920b6d4d96aad8.png

 常量区是用来存放一些字符类字符串类的全局变量。堆区是用来存放malloc手动开辟的空间变量的存储区域。静态区内存放的是static修饰的变量,以及全局变量。栈区是存放函数的形参实参的空间,以及函数内的变量。

很明显这里的this指针应当存放在栈区。

四.类的默认成员函数

4.1概念

默认成员函数就是用户没有显示实现,编译器会自动生成的成员函数称为默认成员函数。一个类,我们不写的情况下会默认生成6个成员函数。分别为,构造函数负责完成初始化工作,析构函数负责完成清理工作,拷贝构造是使用同类对象初始化创建对象,赋值重载只要是把一个对象赋值给另一个对象,取地址重载主要是将普通对象和const对象取地址。

4.2构造函数

构造函数是特殊的成员函数,虽然他的名字叫构造,但并不是以开空间创建对象为目的,而是对象实例化时初始化对象。构造函数的本质是为了替代以前stack类中写Init的功能,构造函数自动调用完美解决了此类问题。

4.2.2构造函数的特点以及实现

函数名与类名相同(在类中单独初始化,且名字为类名)。

无返回值(不需要给任何的类型,c++规定如此)。

对象实例化时会自动调用函数。如下图,d1会自动初始化三个值为1。

338865ae1d564062a3c93ac004d88011.png

b23ece4e660a418f8fd8fb8fa99531cd.png 

 构造函数可以重载,我们可以根据自身需要设置变量的值,以及是否需要缺省参数。

若我们不写,编译器会默认初始化生成默认构造函数,若成员变量为基本类型(char int double 指针等)那么编译器就会报错。若成员变量为自定义类型(先前已经在类中定义过的类型)就可以正常进行初始化。

无参构造函数,全缺省构造函数,默认生成的构造函数,都叫做默认构造函数(不传实参就可以调用),这三个函数有且只能存在一个,不能同时存在。

4.2.3构造函数实现法2

除了用函数体内赋值的方法,构造函数初始化还有一种方法就是初始化列表,初始化列表从一个冒号开始,用逗号分隔数据成员列表,每个成员变量后面跟一个放在括号中的初始值或表达式。(在{}和()中只能初始化一次)

b52330af24f145edbb7310182e8764bd.png

 当面对引用成员,const成员变量,没有默认构造的类类型变量,必须在:下进行定义。因为这些变量已经在主函数中定义过了,无法在构造函数中重新定义,所以只需要在:下定义即可使用。

4.2.4成员变量初始化逻辑

显示在初始化列表的成员变量就按这个值进行初始化。未显示在初始化列表的成员,如果有缺省值或者就按缺省值初始化,若没有缺省值,内置类型成员可能会初始化为随机值,自定义类型成员若有对应的构造函数那么就可以调用若没有则会报错。引用,const,没有默认构造函数的成员必须在初始化列表进行初始化。成员变量会根据声明的顺序进行初始化,所以尽量按照声明顺序进行定义。

4.3析构函数

析构函数与构造函数相反,析构函数是完成对对象本身的销毁,例如我们需要在函数内申请空间,当栈帧销毁时要先做清理资源使得资源释放。

4.3.2析构函数的特点

要在析构函数前加上~。(析构函数要与类同名)

fdc7a8a437b14945a29d110ceaac2eb3.png

 

参数无返回值,且一个类只能有一个析构函数,若未显示定义,系统会自动生成。

对象生命周期结束时,系统会自动调用析构函数。

跟构造函数类似,若我们不写编译器自动生成的析构函数对内置类型不做处理,自定类型成员则调用他们的析构函数。

若类中没有资源申请,析构函数可以不写,直接使用编译器生成的默认析构函数。但是当有资源申请时,一定要自己写析构函数,否则就会造成资源泄露。

4.3.3构造函数和析构函数的顺序

构造函数遵循,先全局变量,再局部变量,然后是局部静态变量。全局变量指在main主函数外的的变量,静态变量指static修饰的变量,而局部变量就指主函数内的变量。遵循从上到下的顺序进行构造。

析构时,遵循先局部再全局的思路,先析构局部的简单变量,再析构静态变量,最后是全局变量

4.4拷贝构造函数

类似于键盘的复制粘贴,用于复制类类型的函数。拷贝构造需要接受拷贝的必须是未存在的对象,而拷贝的必须是存在的对象。

4.4.1拷贝构造函数特点

1.拷贝构造函数是构造函数的一个重载。将函数内部的值copy到拷贝构造函数中。

05e222e9fb2542858a43d9dbe1e7e62c.png

 2.拷贝构造函数的参数只有一个且必须是类类型对象的引用(可以有其他参数,但必须是缺省参数),使用传值方式编译器会报错,因为会在语言逻辑上引发无穷递归调用。

所以在拷贝时必须使用引用直接指向原先的类。

3957bfa61a9444d1868f400939026142.png

 通过加上Const来防止拷贝的值被轻易修改。

若没有使用&符号,每当我们需要拷贝时,就要重新开辟一块空间,调用一次构造函数,此时通过拷贝函数将其copy进来,但是当我们调用拷贝函数时又会重新开辟一块新的空间,反复上述步骤。

3.若未显示定义拷贝构造,那么系统会调用默认的拷贝构造。这里的拷贝构造是浅拷贝,若变量为int,char这些类型的变量,拷贝时会单独开辟一块空间储存他们的值。若变量为malloc出的一块空间,或者指针文件类的指向型,浅拷贝后,还会指向原先的地址。若是这样一旦函数析构,或者变量改变,就会影响到拷贝的值。所以我们需要在拷贝函数上重新单独为其开辟一块新的空间储存。

934f14c7d9244858801e2a274cd48b7a.png

五.赋值运算符重载

5.1运算符重载

当运算符被用于类类型的对象时,编译器无法直接识别运算符定义,所以我们需要重新进行运算符定义。我们用operator和后面要定义的运算符共同构成,他相当于一个函数,拥有返回类型和参数列表以及函数体。重载的运算符函数的参数个数要与运算对象个数一样多,并且按照顺序进行排列。若重载运算函数是成员函数,则它的第一个运算对象默认传给隐形的this指针,所以运算符重载作为成员函数后,参数少一个。运算符重载后,其优先级和结合性与对应的内置类型运算符保持一致。当然也并不是所有的运算符都能重载,像.*    ::   sizeof   ?:      .不能进行重载

0ee4ef4925394e53b948532c17ad2101.png

 如图其中的.*是调用成员函数指针的符号。

一个类需要重载哪些运算符是看哪些运算符重载之后有意义,比如日期减日期可以得到天数,但日期加日期就无意义。

重载运算前置++和后置++时,重载名都是++,无法区分,所以c++规定,后置++重载时,增加一个int形参,跟前置++构成函数重载用于区分。

d4f9461fdc10488eabdf1ca21fe0c25a.png

 5.2赋值运算符重载

赋值运算符重载是一个默认成员函数,用于完成两个已经存在的对象直接拷贝赋值,这里需要跟拷贝构造进行区分,拷贝构造是一个对象拷贝初始化给另一个要创建的对象。

5.2.1特点

1.赋值运算符应当写成const当前类类型的引用,否则传参会有拷贝降低效率。

2.没有显示实现时,编译器会自动生成一个默认赋值运算符重载,默认赋值运算符重载为浅拷贝,对于动态开辟空间的变量会同时使用同一块空间,会导致错误发生。也就是说,当成员内有多个对象享用同一块空间时,需要自行手动实现。

3.手动实现时,需要先释放当前对象已有的资源,以避免内存泄露,对于(int,double)这类型的直接进行赋值,对于指针这类的需要重新开辟空间,进行深拷贝。最后返回当前对象的引用。

4.注意当出现a=a的情况,直接释放资源会导致错误,所以开头先判断两者地址是否重叠再进行拷贝。

代码参考 

87240b5d5a2e4980a04773f899eaedb1.png

5.3日期实现

5.3.1 Date.h

8e0c8dd8b3434bf1876236f6828871d1.png

6b827c414ca143ebbc46743e32012a2e.png 

 其中对于自身有改变的函数需要传递引用值,对于this指针不存在改变的要在函数后添加const修饰。

5.3.2 Date.cpp

35985b828d1e4fb79b29e79599f6899d.png

75c6c05da86e4e60a5c19411bf46e03c.png 

572d58a58dbd41df84e53f167d8facbe.png 

f5177c003eec4586b412901b8d2e4f18.png 

324a2e75e13c4988b06e75166e2d41a5.png 

dcfa6227425a4de5ab5032460694da16.png 

a857d7ccf95d4f5aaa244ea1f72e9cb8.png 

2aae3b47da4c4bd5b10c58995d241084.png 

 对于代码相似的函数实现尽可能的使用函数调用,这样可以减少代码量并且使得程序更加可观。

ostream&和&istream是c++库中定义的一个流输入输出重载的函数名。我们在类中定义友元函数,因为函数调用时是按照严格的左右位置调用的。

六.类型转换

当我们使用=来赋值类类型时,会存在一个隐式的类型转换,开辟一块空间将其进行拷贝函数赋值。

18826b5760b34079924520e67bcec22e.png

 若是遇到引用符号时,因为临时对象具有常性,所以在传值时,左值必须使用const将变量转变为常性。若等号传参时有多个变量则用{}来包含值,若是有多组则以这样的形式传值{{},{}}。若是自定义类型转换则需要相应的构造来支持。

七.static成员

7.1static成员特点

用static修饰的成员变量称之为静态成员变量,静态成员变量一定要在类的外部进行初始化。静态成员变量为所有类对象所共享,不属于某个具体的对象,不存在对象中,存放在静态区。用静态成员修饰的函数没有this指针,意味着函数内部不能访问非静态的成员,只能访问静态成员。而非静态的成员函数可以访问任意的静态成员变量和静态成员函数,只需要进行突破类域。突破类域有两种方法,类名::静态成员或者对象.静态成员。静态成员不走初始化列表,因为他不属于某个对象,也不能进行声明位置的初始化,但需要在声明处进行声明。

八.内部类

内部类就是在一个类里面再封装一个类,当这两个类之间有着紧密联系或者a类是为b类专门实现的时候可以使用(内部类默认是外部类的友元类)。内部类是一个独立的类,与定义在全局相比,它只受外部类类域限制和访问限定符限制,所以外部类的对象中不包含内部类。

九.匿名对象

用类型(实参)定义出来的对象叫做匿名对象,匿名对象的生命周期只在当前一行。

4a0267800767483c88f4bb9291f53a02.png

 若我们想延长匿名对象的生命周期,可以使用const a& b = A()的方法来延长生命。

匿名对象的用法主要服务于,当函数调用时给类类型给缺省值,无法直接给出时。

a06662d773ee4db5a3a205a81cbb6b06.png

 十.对象拷贝时的编译器优化

编译器为了提高程序的运行效率,在不影响正确性的情况下会尽可能减少一些传参过程中可以省略的拷贝。

4b4022a302a449c3a03dd0628075854c.png

 在不优化的版本下,f2中的aa传值过程中,会预先开辟一块空间接收return aa的值,再将值进行一次拷贝构造给回aa2中。在vs2019的debug版本下,则会省略掉中间值的拷贝构造,而是直接将return的返回值拷贝构造给aa2。而在release版本下,则会更加的激进,return返回的aa直接就是aa2的引用返回值,直接省略了空间的开辟。

 

 

 

 

 

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

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

相关文章

jquery-picture-cut 任意文件上传(CVE-2018-9208)

目录 1、漏洞描述 2、访问ip:port 3、一句话木马:exploit.php 4、上传一句话木马 5、中国蚁剑连接成功 6、拿到flag 1、漏洞描述 jQuery是一个快速、简洁的JavaScript框架,是继Prototype之后又一个优秀的JavaScript代码库(框…

【Spring Boot】# 使用@Scheduled注解无法执行定时任务

1. 前言 在 Spring Boot中,使用Scheduled注解来定义定时任务时,定时任务不执行;或未在规定时间执行。 import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component;Component public c…

LM2904运算放大器的应用:测电池电压

在电子设备的广泛应用中,电池作为便携设备的能量来源,其电压监测显得尤为关键。LM2904作为一款低功耗、高增益带宽积和高共模抑制比的双运算放大器,非常适用于电池电压的测量与监测。本文详细介绍了LM2904在电池电压测量方面的应用&#xff0…

电子学习中的关键游戏化元素

游戏化彻底改变了电子学习领域,提供了一种使学习具有吸引力、互动性和有效性的方法。通过将类似游戏的功能集成到教育平台中,教育工作者可以增强动力,提高知识记忆,并创造动态的学习体验。游戏化的关键要素为设计与学习者产生共鸣…

【网络通信】数据集合集!

本文将为您介绍经典、热门的数据集,希望对您在选择适合的数据集时有所帮助。 1 RITA 更新时间:2024-11-22 访问地址: GitHub 描述: RITA 是一个用于网络流量分析的开源框架。 该框架以 TSV 或 JSON 格式提取 Zeek 日志,目前支…

竞赛经验:关于不记得字母表,如何知道字母顺序qwq

利用ASCII码算出码值再转成字符即可 #include <bits/stdc.h> using namespace std;int main() {for(int i 1; i < 30; i){cout << char(ai) << ;} }结果&#xff1a; ps:大意了&#xff0c;本想用电脑目录&#xff0c;但没考虑到会有文件不存在导致缺…

GitLab指定用户分配合并权限

进入项目 -》 Project Settings Repository -》展开 Protected branches -》 添加要保护的分支&#xff0c;设置角色 管理用户角色权限 查看到不同用户的角色&#xff0c;一般设置Developer只有Merger Request权限&#xff0c;Maintainer还有Merge审批权限 GitLab 中的权限…

C语言菜鸟入门·关键字·union的用法

目录 1. 简介 2. 访问成员 2.1 声明 2.2 赋值 3. 共用体的大小 4. 与typedef联合使用 5. 更多关键字 1. 简介 共用体&#xff08;union&#xff09;是一种数据结构&#xff0c;它允许在同一内存位置存储不同的数据类型&#xff0c;但每次只能存储其中一种类型的…

[C++]了解内置类型升级

内置类型升级 1.调用模板T时&#xff0c;为什么可以使用T()类型的匿名对象来传参2.内置类型被升级成为类后的使用事项 1.调用模板T时&#xff0c;为什么可以使用T()类型的匿名对象来传参 当我们在定义或声明一个函数时&#xff0c;如果想使用模板T类型的默认构造&#xff08;例…

JavaScript的基础数据类型

一、JavaScript中的数组 定义 数组是一种特殊的对象&#xff0c;用于存储多个值。在JavaScript中&#xff0c;数组可以包含不同的数据类型&#xff0c;如数字、字符串、对象、甚至其他数组。数组的创建有两种常见方式&#xff1a; 字面量表示法&#xff1a;let fruits [apple…

Neural Magic 发布 LLM Compressor:提升大模型推理效率的新工具

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

友思特新闻 | 友思特荣获广州科技创新创业大赛智能装备行业赛初创组优胜企业!

2024年11月19日&#xff0c;第十三届中国创新创业大赛&#xff08;广东广州赛区&#xff09;暨2024年广州科技创新创业大赛智能装备行业赛颁奖典礼隆重举行。 赛事奖项介绍&#xff1a;广州科技创新创业大赛智能装备行业赛 第十三届“中国创新创业大赛&#xff08;广东广州赛区…

以灵魂的方式进入:从table和drawer哪个单词更长说起

有两个单词&#xff0c;table和drawer&#xff1a; table n.桌子drawer n.抽屉 现在就问&#xff0c;这两个单词&#xff0c;哪个单词更长&#xff1f;你会说&#xff0c;神经&#xff0c;这还用问吗&#xff1f;哪个单词更长&#xff0c;不一目了然吗&#xff1f; 其实&…

车载测试核心知识点和面试题

今天为大家分享一下车载测试岗位面试的时候&#xff0c;一定会问的相关技术。这些工具在测试的工作中会用到&#xff0c;在面试中也会经常被问到。所以同学们一定要去实战操作&#xff0c;这样理解和吸收才会更加深刻。 一、车载仪表台架测试CANoe工具实战 我们知道&#xff…

C/C++逆向:虚函数逆向分析

虚函数&#xff08;Virtual Function&#xff09;是C中实现多态的一种机制&#xff0c;它允许在运行时通过基类的指针或引用调用派生类中的函数&#xff0c;而不是基类中的版本。虚函数通常与继承和多态结合使用。通过在基类中使用 virtual 关键字声明函数&#xff0c;允许派生…

es写入磁盘的过程以及相关优化

数据写入到内存buffer同时写入到数据到translog buffer,这是为了防止数据不会丢失每隔1s数据从buffer中refresh到FileSystemCache中,生成segment文件,这是因为写入磁盘的过程相对耗时,借助FileSystemCache,一旦生成segment文件,就能通过索引查询到了refresh完,memory bu…

linux部署Whisper 视频音频转文字

github链接&#xff1a;链接 我这里使用anaconda来部署&#xff0c;debian12系统&#xff0c;其他linux也同样 可以使用gpu或者cpu版本&#xff0c;建议使用n卡&#xff0c;rtx3060以上 一、前期准备 1.linux系统 链接&#xff1a;debian安装 链接&#xff1a;ubuntu安装 …

论文阅读:A Software Platform for Manipulating theCamera Imaging Pipeline

论文代码开源链接&#xff1a; A Software Platform for Manipulating the Camera Imaging Pipelinehttps://karaimer.github.io/camera-pipeline/摘要&#xff1a;论文提出了一个Pipline软件平台&#xff0c;可以方便地访问相机成像Pipline的每个阶段。该软件允许修改单个模块…

【科研绘图】Matplotlib 教学

以下是一个针对 Matplotlib 教学 的博客结构&#xff0c;按照分步骤教学方式撰写&#xff0c;以帮助读者从基础到高级逐步掌握 Matplotlib。 Matplotlib 教学&#xff1a;从基础到进阶绘图 Matplotlib 是 Python 中功能强大的数据可视化库&#xff0c;可以用来绘制多种类型的图…

【网络系统管理】2023年全国职业院校技能大赛:组策略--10套题组合--4

16、只有域管理员和IT部门员工可以登陆服务器 (1)计算机配置\策略\Windows设置\安全设置\本地策略\用户权限分配 17、创建ChinaSkills23为GPO管理员,加入到企业管理、域控管理员组 (1)gpmc.msc\林\域\%domain%--在这个域中创建GPO 18、为所有域用户设置漫游文件 (1)用…