C++类与对象—上

本期我们来学习类与对象

目录

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

类的引入

 访问限定符

类的定义

封装

类的作用域

类的实例化

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++是自动挡,电脑程序控制变速箱
以上即为本期全部内容,希望大家可以有所收获
如有错误,还请指正

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

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

相关文章

ajax写法和json的知识点

1. JQuery方式来实现AJAX 1.1 $.ajax()方式来实现AJAX 语法&#xff1a;$.ajax(url,[settings]);但是我们一般这么写$.ajax({键值对});。 $.ajax()来实现ajax的案例&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"…

第二十六章 案例TodoList 之实现Footer组件

本小节&#xff0c;我们来实现最后的Footer组件的功能&#xff0c;它的功能主要有&#xff1a; 记录已完成和全部的任务列表数量点击【复选框】可以实现全选和全不选点击【删除已完成】按钮&#xff0c;可以将选中的任务项删除掉 实现已完成和全部的任务列表数量 步骤1&#…

OJ刷题 第十二篇

21308 - 特殊的三角形 时间限制 : 1 秒 内存限制 : 128 MB 有这样一种特殊的N阶的三角形&#xff0c;当N等于3和4时&#xff0c;矩阵如下&#xff1a; 请输出当为N时的三角形。 输入 输入有多组数据&#xff0c;每行输入一个正整数N&#xff0c;1<N<100 输出 按照给出…

【技巧】如何修改PDF文件?

PDF文件格式安全、标准化&#xff0c;很多人在工作中几乎离不开。可有些小伙伴想要修改PDF文件内容时&#xff0c;发现无法修改&#xff0c;那是什么情况呢&#xff1f;如何才能修改PDF文件呢&#xff1f;下面小编就来分享一些小技巧。 技巧一&#xff1a;使用PDF编辑器 如果使…

美国肝素钠专用树脂,医药肝素钠提取工艺专用树脂

具有控制孔径的大孔强碱性Ⅰ型阴离子交换树脂 Tulsimer A-72 MP 是一款具有便于颜色和有机物去除的控制孔径的&#xff0c;专门开发的大孔强碱性Ⅰ型阴离子交换树脂。 Tulsimer A-72 MP专门应用于去除COD以及其他有机物等。 Tulsimer A-72 MP 由于其本身的大孔特性而显示出…

差分运算放大电路原理解析

差分运算放大电路&#xff0c;对共模信号得到有效抑制&#xff0c;而只对差分信号进行放大&#xff0c;因而得到广泛的用。 注&#xff1a; &#xff08;1&#xff09;共模信号   共模信号&#xff08;common mode signal&#xff09;是指同时作用于多个电路或电子设备上的信…

Spring

目录 &#x1f43c;今日良言:道阻且长,行则将至 &#x1f407;一、Spring介绍 &#x1f407;二、Spring创建和使用 &#x1f407;三、Spring读取和存储对象 &#x1f407;四、Bean作用域和生命周期 &#x1f43c;今日良言:道阻且长,行则将至 &#x1f407;一、Spring介绍…

IEEE旗下SCI审稿流程及状态详细解读 (附科协高质量IEEE期刊目录)~

能够成功发表一篇IEEE旗下SCI论文 (尤其是TRANS系列) 是很多电气电子工程、计算机及通信领域科研工作者的梦想。很多学者初次投稿IEEE后&#xff0c;会不停登录投稿系统查看状态&#xff0c;其实不必如此心急&#xff0c;只需掌握几个重要的时间节点&#xff0c;定期登录系统查…

一文详解Spring事务传播机制

目录 背景 Spring事务 Transactional注解 使用场景 失效场景 原理 事务传播机制 处理嵌套事务流程 主事务为REQUIRED子事务为REQUIRED 主事务为REQUIRED子事务为REQUIRES_NEW 主事务为REQUIRED子事务为NESTED 实现方式 源码解析 背景 我们在使用Spring管理数据库…

项目结束倒数2

今天,解决了,多个点的最短路问题 用的dfs,配上了floyed计算出的广源距离 难点是要记录路线,dfs记录路线就很烦 但是好在结束了,经过无数的测试,确保没啥问题(应该把) 来看看我的代码 void dfs(int b[], int x, int* sum, int last, int sums, int a[], BFS& s, Floyd_A…

零基础搭建私人影音媒体平台【远程访问Jellyfin播放器】

Yan-英杰的主页 悟已往之不谏 知来者之可追 C程序员&#xff0c;2024届电子信息研究生 目录 1. 前言 2. Jellyfin服务网站搭建 2.1. Jellyfin下载和安装 2.2. Jellyfin网页测试 3.本地网页发布 3.1 cpolar的安装和注册 3.2 Cpolar云端设置 3.3 Cpolar本地设置 4.…

springboot+RateLimiter+AOP自定义注解限流

springbootRateLimiterAOP自定义注解限流 RateLimiter简介springboot集成RateLimiterpom.xml引入RateLimiter常用api代码实现自定义注解Limiter限流切面验证 RateLimiter简介 RateLimiter是Guava库中的一个限流器&#xff0c;它提供如下功能&#xff1a; (1)基于PPS进行限流 (…

ext-1:PDK工具包编译出例程

1、TI的单独StarterWare不更新后&#xff0c;后续维护和更新的是 PROCESSOR-SDK-AM335X 软件开发套件 &#xff08;PDK&#xff09;&#xff0c;对比以前的&#xff0c;里面没有例程&#xff0c;所以下载安装完需要自己编译出example例程。 因为编译出example例程中间会出现很…

深元边缘计算盒子在社区的应用,提高社区的安全性和生活质量

近年来&#xff0c;随着人工智能技术的不断发展和普及&#xff0c;越来越多的社区开始应用边缘计算盒子AI视觉分析技术&#xff0c;以提高社区的安全性和管理效率。本文将介绍边缘计算盒子AI视觉分析技术在社区中的应用及其优势。 一、边缘计算盒子AI视觉在社区中的应用 1.安防…

C++类的模拟实现

&#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;C &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 本博客主要内容讲解了简单模拟实现string类 C类的模拟实现 文章目录 C类的…

centos7部署FastDFS服务

一、安装需要的相关依赖 yum -y install make cmake gcc gcc-c 因为我的服务器已经安装了gcc&#xff0c;所以略去 使用gcc -v查看版本 yum -y install zip unzip 安装性能事件通知库 yum -y install libevent 安装nginx依赖 yum -y install libevent yum -y install zli…

OSCP-Fail(rsync、fail2ban提权)

目录 扫描 rsync 提权 扫描 rsync 基于nmap,确信将进一步研究rsync。 为此,将使用netcat使用的rsync枚举。 使用netcat,我们可以列出rsync托管的当前共享。 我们看到“fox”和“fox home

大数据管理中心规划设计方案(ppt可编辑)

本资料来源公开网络&#xff0c;仅供个人学习&#xff0c;请勿商用&#xff0c;如有侵权请联系删除。 统一汇聚 推动业务数据协同5 价值提炼 支撑精准服务与科学管理6 实时感知 辅助城市治理高效运行7 大数据资源平台目标体系规划11 建设目标与思路12 使能高效协同&#xff0…

PLM听过很多遍,却依旧不知道是什么?看完这篇你就懂

上周参加展会&#xff0c;很多客户在现场了解到e企拆图解决方案后&#xff0c;向我们咨询了很多问题&#xff0c;发现有几个名词经常被提及&#xff0c;比如PLM、PDM、BOM等。随着技术的爆炸发展&#xff0c;新的名词概念也与日俱增&#xff0c;对于这些名词&#xff0c;可能我…

Sa-Token源码简单阅读

一.权限登录模块包括几个基本子模块&#xff1a; 1.登录。 实现方式大致为&#xff1a;先检验用户名密码是否正确&#xff0c;如正确则在缓存中存入用户信息&#xff08;一般必须要有用户标识和访问token&#xff0c;或再加一些附加信息如用户的角色权限&#xff09;&#xf…