C++类与对象—上

news/2024/4/28 2:08:12/文章来源:https://blog.csdn.net/KLZUQ/article/details/130288198

本期我们来学习类与对象

目录

面向过程和面向对象初步认识

类的引入

 访问限定符

类的定义

封装

类的作用域

类的实例化

this指针

C语言和C++实现Stack的对比 


面向过程和面向对象初步认识

C 语言是 面向过程 的, 关注 的是 过程 ,分析出求解问题的步骤,通过函数调用逐步解决问题。
比如我们洗衣服

 这是C语言的的实现,我们要按步骤,一步一步来求解

C++ 基于面向对象 的, 关注 的是 对象 ,将一件事情拆分成不同的对象,靠对象之间的交互完成。

 再比如我们要设计一个外卖系统

如果是面向过程的话,就是上架,点餐,送餐,结算等等,关注的是步骤

而如果是面向对象的话,就是商家,骑手,客户三者之间的相互关系了,关注的是对象与对象之间关系和交互,就像在虚拟的世界里描述现实世界一样,是将现实世界的类和对象映射到计算机里

大家可以认为面向对象是一种更高级的开发方式,是语言的进化方向,现在新的语言几乎都是面向对象的,所以这也是我们学习的方向

类的引入

我们之前说过,C++里将结构体升级为了类

s1是我们以前创建栈的方式,因为现在的栈是一个类了,所以我们可以直接用类名来创建栈,如上面的s2 

我们栈里面的a,capacity,top都叫做成员变量

 此外,C++还可以在类里面定义函数,这叫做成员函数(方法)

我们之前为了区分,在各种函数前加上了前缀,比如StackInit,QueueInit,现在可以在类里面定义函数,我们就不需要加前缀了

两个函数都叫Init,但是因为域不同,所以不会干扰,C++里{ } 定义的都是域

我们随便写点函数

这里成员变量并不一定要在函数后面,在前面也是可以的,甚至写在中间也没问题,因为类域是一个整体

我们再调用函数就非常方便了,用 . 的方式即可,我们写起来是比C语言舒服的

上面结构体的定义, C++ 中更喜欢用 class 来代替

但我们换成class后又编译不通过了,这就涉及到了权限的问题

 访问限定符

【访问限定符说明】
1. public 修饰的成员在类外可以直接被访问
2. protected private 修饰的成员在类外不能直接被访问 (我们暂时认为 protected private 是类似的 )
3. 访问权限 作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
4. 如果后面没有访问限定符,作用域就到 } 即类结束。
5. class 的默认访问权限为 private struct public( 因为 struct 要兼容 C)
注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别

 

我们加上public就可以编译通过了,pubilc是公有的,直到遇到下一个访问限定符前都是公有的,但我们一般不希望成员变量也是公有的,所以要加上private

 说明白点就是,我们想给别人用的是公有,不想给的是私有

我们切换成struct也是可以用访问限定符的,因为struct也是类,区别在于struct默认是公有,class默认是私有,所以我们之前用struct可以通过编译,而class不可以,不过我们不推荐用默认,最好显示的指定出来

类的定义

class className
{// 类体:由成员函数和成员变量组成}; // 一定要注意后面的分号
class 定义类的 关键字, ClassName 为类的名字, {} 中为类的主体,注意 类定义结束时后面 分号不能省
类体中内容称为 类的成员: 类中的 变量 称为 类的属性 成员变量 ; 类中的 函数 称为 类的方法 或者 成员函数
类的声明和定义是可以分离的,我们来看一看

 需要注意的是,我们在定义时要加上类域,让编译器知道,这不是一个普通函数,而是一个类的成员函数

另外,如果我们在类里面直接定义函数,就默认是内联,这些函数前加不加inline都无所谓

比如我们的Push和Destroy都是内联,当然最终是否成为内联要取决于编译器

所以我们长的函数定义和声明分离,短的函数就直接定义了

关于类还有一个问题

这里的year是无法区分的,所以我们喜欢在成员变量前加上下划线

后面加下划线也是可以,也有人喜欢加m

其他方式也可以,大家根据喜好选择就行 

封装

面向对象的三大特性: 封装、继承、多态
封装是将数据和方法放在一起,不想给你看的变成私有,想给你看的变成公有,封装的本质是一种更好的管理
比如电脑,我们最后使用的只有鼠标,键盘,显示器等等,但电脑真正工作的是cpu,显卡,内存等等一些硬件元件,我们并不需要关系主板上的线路有些什么,cpu是如何工作的等等
我们举个例子,我们去参观一些景点,博物馆时,有些东西会被保护起来,被围起来,如果不将他们保护起来,我们可能就会看到那些地方刻着xxx到此一游,xxx love xxx等等,有了封装,我们按照规则来游玩,就是一种更好的管理
C语言的数据和方法是分离的,C++是没有分离的,我们在C语言时写的栈,Top函数哪怕只有一行,我们也要写成函数

我们正常用是这样用的

但是如果有人这样写呢?

一会调用函数,一会自己访问

更严重一点的,如果这样搞,就可能会出现随机值了,你怎么知道top是栈顶元素,还是下一个位置呢?

 但是如果我们没有提供STTop函数,让用的人自己去想top是什么,就会出现各种各样的问题

所以C++为了解决这些问题,为了杜绝这种乱写代码的情况产生,就有了封装

我们只能按照规则访问,不遵循规则就报错

类的作用域

类定义了一个新的作用域 ,类的所有成员都在类的作用域中 在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。
class Person
{
public:void PrintPersonInfo();
private:char _name[20];char _gender[3];int _age;
};
// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{cout << _name << " "<< _gender << " " << _age << endl;
}
另外,局部域和全局域会影响生命周期,而类域和命名空间不会

类的实例化

我们先看这样一个问题

我们在类里面的成员变量是属于声明还是定义? 

答案是声明,对于变量而言,声明和定义的真正区别在于开不开空间

 而且对于类,是整体定义,而不是一个一个的定义

我们这里创建的s,叫做类的实例化对象,或者对象的定义

类就像房子的设计图一样,而对象就是我们根据设计图设计出的房子

类里面能不能存数据?不能,就像图纸里面不能住人

 所以这种错误就是错误的,因为它没有空间,图纸不能住人

我们再看一个问题

 这里应该输出多少呢?是只算成员呢,还是也要算函数呢?

我们看运行结果

 结论:对象的大小只算成员变量(要结构体对齐),不算成员函数

另外,sizeof对象和sizeof类计算出的结果是一样的,就和sizeof(int)是一样的

我们举个例子

我们先把成员变量设置为公有的,我们来看

s1.top和s2.top,并不是同一块空间,就像两栋房子,都有厨房,都有卧室

但是两个Push,是同一个函数,就像我们在小区里,我们的篮球场,篮球场不需要每家每户都建一个,而是在公共的地方建一个,大家一起来使用,篮球场也可以在每家每户建一个,但是没必要,太浪费了,所以调用函数,就是去公共的区域调用

我们再看这个问题

 为什么这里是1,而不是0呢?

举个例子就是我们在小区里建房子时,有一栋房子我们还没规划好,但是需要给这栋房子留个位置,不然之后就无法建造

 这一个字节就是用来占位的,表示对象存在,不然取地址就没办法了

this指针

我们先看这样一段代码

class Date
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year; // 年int _month; // 月int _day; // 日int a;
};
int main() {Date d1, d2;d1.Init(2022, 1, 2);d2.Init(2023, 2,3);d1.Print();d2.Print();
}

 我们知道d1,d2是两个不同的对象,但是Print函数是在公共区域的,为什么输出的结果却不同呢?

这里面就有一个隐藏的this指针,编译器会将函数处理为

 同时,函数的调用也会被处理为

 这些都是编译器的暗箱操作

另外,还规定了我们不能在形参和实参上显示的去写,所以上面都是报错

但是可以显示的去用,比如

 我们也不能将this改为空指针

 因为this指针的类型是 类类型* const

补充两种错误的写法

 

 上面的是声明,即我们不能在图纸里住,第二个是 :: 这个符号是域作用限定符

this指针是存在哪里的?对象,栈,堆,静态区还是常量区?

答案是在栈里面,this指针是形参,形参是在栈里的

 vs下对this指针的传递进行优化,对象的地址是放在ecx,ecx存储this指针(ecx是寄存器)

注意,这是vs下面的,不是所有的编译器

我们再看一个问题

 答案为第一段代码选C,第二段代码选B

因为第一段代码p调用Print不会解引用,Print的地址不在对象里,不过p会作为实参传递给this指针

 传递空指针并不会报错

第二段代码同样p调用Print不会解引用,但是this传递过去后,this是空指针,函数内部访问了_a,就是空指针解引用

总结一下

1. this 指针的类型:类类型 * const ,即成员函数中,不能给 this 指针赋值。
2. 只能在 成员函数 的内部使用
3. this 指针本质上是 成员函数 的形参 ,当对象调用成员函数时,将对象地址作为实参传递给 this 形参。所以对象中不存储 this 指针
4. this 指针是 成员函数 第一个隐含的指针形参,一般情况由编译器通过 ecx 寄存器自动传递,不需要用 户传递

C语言和C++实现Stack的对比 

C语言实现

 我们发现用C语言实现栈(这里我只截了开头和结尾,相信大家都知道C实现的栈是什么样子)

每个函数的第一个参数都是 Stack*
函数中必须要对第一个参数检测,因为该参数可能会为 NULL
函数中都是通过 Stack* 参数操作栈的
调用时必须传递 Stack 结构体变量的地址
结构体中只能定义存放数据的结构,操作数据的方法不能放在结构体中,即 数据和操作数据的方式是分 离开的 ,而且实现上相当复杂一点,涉及到大量指针操作,稍不注意可能就会出错。
C++实现
typedef int DataType;
class Stack
{
public:void Init(){_array = (DataType*)malloc(sizeof(DataType) * 3);if (NULL == _array){perror("malloc申请空间失败!!!");return;}_capacity = 3;_size = 0;}void Push(DataType data){CheckCapacity();_array[_size] = data;_size++;}void Pop(){if (Empty())return;_size--;}DataType Top() { return _array[_size - 1]; }int Empty() { return 0 == _size; }int Size() { return _size; }void Destroy(){if (_array){free(_array);_array = NULL;_capacity = 0;_size = 0;}}
private:void CheckCapacity(){if (_size == _capacity){int newcapacity = _capacity * 2;DataType* temp = (DataType*)realloc(_array, newcapacity *sizeof(DataType));if (temp == NULL){perror("realloc申请空间失败!!!");return;}_array = temp;_capacity = newcapacity;}}
private:DataType* _array;int _capacity;int _size;
};
int main()
{Stack s;s.Init();s.Push(1);s.Push(2);s.Push(3);s.Push(4);printf("%d\n", s.Top());printf("%d\n", s.Size());s.Pop();s.Pop();printf("%d\n", s.Top());printf("%d\n", s.Size());s.Destroy();return 0;
}
C++ 中通过类可以将数据 以及 操作数据的方法进行完美结合,通过访问权限可以控制那些方法在类外可以被 调用,即封装 ,在使用时就像使用自己的成员一样,更符合人类对一件事物的认知。而且每个方法不需要传 递Stack* 的参数了,编译器编译之后该参数会自动还原,即 C++ Stack * 参数是编译器维护的, C 语言中需 用用户自己维护
我们可以理解为C和C++的区别就是手动挡和自动挡的区别
C就是手段当,需要自己控制变速箱,C++是自动挡,电脑程序控制变速箱
以上即为本期全部内容,希望大家可以有所收获
如有错误,还请指正

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

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

相关文章

buuctf -2

目录 你竟然赶我走 大白 N种方法解决 [ACTF2020 新生赛]Include 1 php://filter的一些学习 [ACTF2020 新生赛]Exec [强网杯 2019]随便注 你竟然赶我走 1.下载文件&#xff0c;得到一张图片 2.放进010分析&#xff0c;在文件尾得到flag 大白 1.根据题目提示&#xff0…

Pyqt应用相关之与Excel的联接

利用所学相关Pyqt实现对于Excel文件内部数据的处理&#xff0c;首先需要获取Excel的数据&#xff0c;在获取后进行保存处理即可完成相应的操作。 def save_data_btn_click(self):dir self.save_dir_text.text().strip()self.data_frame_group.to_excel(dir output.xlsx,sheet…

HCIP之链路聚合、VRRP

链路聚合 链路聚合 --- 可以将多个物理接口绑定成一个逻辑接口&#xff0c;即将N条物理链路聚合为一条逻辑链路。可以在不升级硬件的条件下&#xff0c;达到增加带宽的效果 我们将逻辑链路&#xff0c;称为聚合链路&#xff0c;在华为设备中称为ETH-TRUNK链路&#xff08;这个技…

物联网产品的开发的难点,致命点是什么?

物联网产品的开发的难点&#xff0c;致命点是什么&#xff1f; 当下是万物互联的时代&#xff0c; 物联网产品本身的难度因行业而异。但是物联网设备上云通信交互就成了各个行业需要首先解决的问题。 物联网通信问题从产品设计一开始&#xff0c;如果不能很好的解决&#xff0c…

马云上三路和下三路

马云的上三路、下三路&#xff0c;马云最牛搭档总结 马云刚最牛搭档&#xff1a;蔡崇信&#xff0c;关明生 《关乎天下》是关明生写的一本书 趣讲大白话&#xff1a;没有方法走不远 【趣讲信息科技143期】 **************************** 马云上三路&#xff1a;使命&#xff0c…

你掌握了stream流的全部新特性吗?

我们知道很早之前java8对于之前的版本更新了许多 新的支持&#xff0c;比如lamda函数式接口的支持&#xff0c;支持更多函数式接口的使用&#xff0c;对链表&#xff0c;数组&#xff0c;队列&#xff0c;集合等实现了Collectio接口的数据结构提供了StreamSupport.stream()支持…

基于ubuntu18.04.6 LTS服务器安装nvidia驱动

1对于一个刚刚配置的服务器&#xff0c;首先nvidia-smi&#xff0c;自然无法显示Driver Version、最高cuda版本等信息。 nvidia-smi: command not found 需要我们自己安装nvidia驱动 2禁用老驱动 禁用自带nouveau驱动 sudo vim /etc/modprobe.d/blacklist.conf 打开后在CONF文…

2023年软件测试的前景?测试工程师技能提升,进阶自动化测试...

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

【开发日志】2023.04 ZENO----Composite----CompNormalMap

CompNormalMap 将灰度图像转换为法线贴图 将灰度图像转换为法线贴图是一种常见的技术&#xff0c;用于在实时图形渲染中增加表面细节。下面是一个简单的方法来将灰度图像转换为法线贴图&#xff1a; 加载灰度图像&#xff0c;并将其转换为浮点数值范围[0, 1]。 对于每个像素…

IT_开发提测标准规范

背景 公司 IT 规模小&#xff0c;开发提测质量差&#xff0c;流程不规范&#xff0c;导致测试任务重&#xff0c;于是推行 &#xff1a;IT_开发提测标准规范&#xff0c;正文如下&#xff1b;拟定开发提测标准规范后&#xff0c;测试与项目经理内部评审后&#xff0c;发至IT群…

盘点几款还不错的企业网盘产品

企业网盘的出现&#xff0c;为企业提供文件安全管理&#xff0c;团队协作服务&#xff0c;解决了便捷性与安全性等问题&#xff0c;受到了企业的青睐。市面上的企业网盘工具也是五花八门&#xff0c;我们该如何选择适合自己团队的网盘工具呢&#xff1f; 本文盘点了几款还不错的…

反射-Class类分析

反射相关的主要类 java.lang.Class&#xff1a;代表一个类&#xff0c;Class对象表示某个类加载后在堆中的对象java.lang.reflect.Method&#xff1a;代表类的方法&#xff0c;Method对象表示某个类的方法java.lang.reflect.Field&#xff1a;代表类的成员变量&#xff0c;Fie…

20230422 | 24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 02.07. 链表相交、142. 环形链表 II

1、24. 两两交换链表中的节点 初始时&#xff0c;cur指向虚拟头结点&#xff0c;然后进行如下三步&#xff1a; 操作之后&#xff0c;链表如下&#xff1a; 看这个可能就更直观一些了&#xff1a; /*** Definition for singly-linked list.* public class ListNode {* i…

Android 日志框架使用

在实际开发中&#xff0c;经常会遇到需要打印日志并保存到文件中&#xff0c;便于后面取日志分析代码运行情况&#xff0c;当然如果只是打印日志不需要记录文件&#xff0c;使用android自带的log工具就完全够了&#xff0c; Log打印日志会记录到系统日志中&#xff0c;可以取出…

Rust之泛型、特性和生命期(一):基本概念

开发环境 Windows 10Rust 1.69.0 VS Code 1.77.3 项目工程 这里继续沿用上次工程rust-demo 泛型、特性和生命期 每种编程语言都有有效处理概念重复的工具。在Rust中&#xff0c;一个这样的工具就是泛型&#xff1a;具体类型或其他属性的抽象替身。我们可以表达泛型的行为或…

CorelDRAW 2023版本更新内容及安装详细教程

这里是CorelDRAW 2023版本更新内容及安装详细教程: CorelDRAW 2023是最新更新版本,在界面和功能上做了较大提升与优化: 1. 简洁界面:采用全新设计界面,简约而不简单。菜单和工具栏进行了整合与重组,更加直观。拥有自动标记和提示,易于上手使用。 2. 全新工作空间:提供“轻量…

基于51单片机的脉搏测量仪设计与实现

目录 前言 一、设计背景 二、系统功能 三、系统硬件设计 3.1 总体方案设计 3.2 信号采集电路设计 3.3 报警电路设计 3.4 下载电路 3.5 电源电路设计 3.6 OLED显示设计 3.7 键盘电路 四、系统软件设计 4.1 系统主程序设计 4.2 脉搏采集子程序设计 4.3 键盘程序设…

正式开赛|2023年“桂林银行杯”数据建模大赛暨全国大学生数学建模竞赛广西赛区热身赛

为学习贯彻党的二十大工作报告中关于加快发展数字经济、促进数字经济和实体经济深度融合的重要指示&#xff0c;不断推进数字化转型与金融科技创新&#xff0c;桂林银行联合全国大学生数学建模竞赛广西赛区组委会、广西应用数学中心&#xff08;广西大学&#xff09;共同主办20…

使用EasyExcel导出模板并设置级联下拉及其原理分析

一、概述 项目中有时会遇到需要导出一个Excel模板&#xff0c;然后在导出的Excel中填充数据&#xff0c;最终再调用接口批量把Excel中的数据导入到数据库当中的需求。 其中级联下拉选择&#xff0c;手机号校验&#xff0c;性别校验等都是比较常见的校验。 这里就已上面三种情…

王道计组(23版)3_存储系统

概述 RAM&#xff1a;随机存储器&#xff0c;任一个存储单元可以随机存取&#xff0c;易失。用作主存(DRAM)或Cache(SRAM) ROM&#xff1a;只读存储器&#xff0c;可随机读出&#xff0c;写入较慢&#xff0c;需刷新&#xff0c;非易失。Flash、SSD固态硬盘、U盘 _____SSD&…