【CPP】类 -- class 使用思想

news2025/1/11 5:53:37

目录

      • 3 类 -- class
        • 3.1 什么是类
        • 3.2 类的定义
        • 3.3 类内参数的使用
        • 3.4 类与结构体 -- class VS struct
        • 3.5 类的实操 -- log类(日志类)
        • 3.6 类定义 与 `static`

3 类 – class

3.1 什么是类
  • 类是面向对象的一种设计,其中封装了我们对对象的"描述",或者说对象的"特征"
  • 比方说我们要描述一个游戏玩家,那么这个玩家至少要有以下"特征"
    • 玩家在地图中的位置
    • 玩家的速度
    • 等等
  • 在本节我们也会不断地举玩家这个例子
3.2 类的定义
class player
{
    int x, y;
    int speed;
};

int main()
{
    player player1; //创建一个player变量
}
  • player player1; //创建一个player变量一般称创建一个新变量的过程为"实例化",新的变量称为"对象"
  • 类的基础定义其实和结构体没什么两样,但以下我们来讲讲类的进阶玩法
3.3 类内参数的使用
  • 假设此时玩家移动了,我们想计算出玩家移动后的位置,我们定义一个Move函数
  • 注意:这里如果想调用类内的参数,像结构体一样在变量后加.就行
void Move(player& player1, int& xa, int& ya)    //xa,xy为位置偏移量
{
    player1.x += xa * speed;
    player1.y += ya * speed;
}
int main()
{
	player player1;
	player1.x = 10;
	player1.y = 0;
	player1.speed = 2;

	Move(player1, 1, 1);

	return 0;
}
  • 但如果你此时调用Move函数你会发现编译器报错了
    请添加图片描述

  • 这里报错的原因是,如果我们想使用player中的变量,需要给player中的变量打上public的标签,来表明这个变量允许给类的外部使用

void Move(player& player1, int& xa, int& ya)    //xa,xy为位置偏移量
{
public:
    player1.x += xa * speed;
    player1.y += ya * speed;
}
  • 此时编译器就不会再报错了
    请添加图片描述

  • Move既然只给player用,那我们就可以把Move封装进player中,以后这个Move就专门给player用了,封装进player中之后,player中的变量不用打上public的标签也能正常调用Move,能让变量更加安全一些(当然,以下示例中没有删除掉public是因为创建完变量后还要初始化)

  • 默认类中参数为"私有"

  • 打上public标签之后的类中参数将会变成"公有"

  • 那么将函数封装进类中,此时我们称这个函数为"方法"

class player
{
public:
	int x, y;
	int speed;

	void Move(int xa, int ya)
	{
		x += xa * speed;
		y += ya * speed;
	}
};
  • 此时我们如果想使用这个方法,只需要在变量后加.即可
int main()
{
	player player1;
	player1.x = 10;
	player1.y = 0;
	player1.speed = 2;

	player1.Move(1, 1);

	return 0;
}
3.4 类与结构体 – class VS struct
  • 上文我们提到过类与结构体非常相似,事实上排除默认给类内参数提供public的话,类和结构体并没有本质区别,事实上他俩就是一样的,如果咱写过C实现 [贪吃蛇][贪吃蛇链接] 的话,其实在其项目中结构体的使用思想就是面向对象的思想,和类的使用是非常相似的(函数也可以封装进结构体成为方法)
  • 上文我们提到过,我们可以使用public将默认为"私有"的类内参数改为"公有",而结构体的参数默认为"公有",那么相反,我们可以使用private将结构体内参数改为"私有",能够看到,参数改为私有之后调用就会报错了
struct struct_player
{
private:
	int x, y;
	int speed;

	void Move(int xa, int ya)
	{
		x += xa * speed;
		y += ya * speed;
	}
};
int main()
{
	struct struct_player player2;
	player2.x = 10;
	player2.y = 0;
	player2.speed = 2;

	player2.Move(1, 1);

	return 0;
}

请添加图片描述

  • 虽然在代码上类与结构体并没有本质上差别,但实际在使用中,他们俩的差别还是非常大的
  • CPP存在结构体这个玩意,纯粹是为了兼容C而做出的妥协,如果你只使用CPP中"更新"的内容,那完全可以删除结构体而不影响使用,甚至说你可以用#define把所有的struct替换成class,那你的代码中就不会存在struct这个东西了
  • 所以说我们在实际使用中是使用类还是结构体,纯粹是个人的编程风格,你想把struct当作class用,也没问题,你想把class当作struct用,也没问题,纯粹取决于自己
  • 比较通用的风格是,结构体只用来储存数据,不用来做任何"继承"操作(后面的文章咱们会提到的),例如我们想保存一个玩家在地图上的位置
typedef struct position
{
	int x, y, z;
	void Move(int& x, int& y, int& z, struct offset& os)
	{
		x += os.x;
		y += os.y;
		z += os.z;
	}
}position;
  • 这里这个结构体仅仅只是数据的载体,而非类
  • 比方说游戏CS2中,有类player,阵营分为"反恐精英"和"恐怖分子",每个阵营的玩家有不同的模型,击杀提示,带不带"包"等等,两个阵营的玩家统统继承自player这个大类,而结构体只用来存数据,它不是一个比较广的概念
  • 数据集用结构体,继承用类

请添加图片描述

3.5 类的实操 – log类(日志类)
  • 日志类用于根据消息的不同等级,向控制台输出不同等级的信息

  • 起初日志会分为三个等级,分别为"错误",“警告”,“消息及跟踪”

  • 我们需要在log类内定义方法,使创建新log变量的时候能让其拥有某个具体等级,算是…“继承”?

  • 向这个类传不同等级的信息而使其输出不同的等级的信息

  • 信息等级不匹配变量的等级就报错

class Log 
{
public: //将枚举类型转为公共使得其他函数也能用它
	enum LogLevel //枚举类型用来规定等级
	{
		info, //普通消息
		warning, //警告
		erorr, //报错
	};

private:
	LogLevel Level = info; //当前变量的等级(记得给个默认等级)

//把方法转为公共,否则不能被类外的其他函数调用
//如果你在类中定义多个函数且是层层调用的话,只用公共最外层的接口就行了
//不转为公共的话这些函数就只能被类中其他函数调用了
public: 
	void SetLogLevel(const LogLevel seting_level) //用于实例化变量后赋予其他 等级
	{
		Level = seting_level;
	}

	void Warn(const char* a) //如果当前log等级大于warning的话就允许输出warn信息
	{
		if (Level >= warning)
		{
			std::cout << "[WARNING]: " << a << std::endl;
		}
		else //否则输出等级错误
		{
			std::cout << "Log_Level error" << std::endl;
		}
	}

	void Err(const char* a) //如果当前log等级大于error的话就允许输出err信息
	{
		if (Level >= erorr)
		{
			std::cout << "[ERROR]: " << a << std::endl;
		}
		else //否则输出等级错误
		{
			std::cout << "Log_Level error" << std::endl;
		}
	}

	void Info(const char* a) //如果当前log等级大于info的话就允许输出info信息
	{
		if (Level >= info)
		{
			std::cout << "[INFO]: " << a << std::endl;
		}
		else //否则输出等级错误
		{
			std::cout << "Log_Level error" << std::endl;
		}
	}
};
int main()
{
	Log log;

	log.SetLogLevel(log.info); //设置初始等级
	log.Info("hello world!"); //打印符合该等级下的信息

	log.Err("hello world!"); //打印不符合该等级下的信息

	return 0;
}

请添加图片描述

  • 不难看出,定义类这个操作只是创建一个"模具",真正造一个能用的变量出来还需要把一些参数扔到模具里面套(某种角度上说还是结构体那套逻辑)
3.6 类定义 与 static
  • 和函数中的静态变量一样,无论外部如何改变,类中的静态变量有且只存在一个

  • 封装性:

    • 封装性是指将对象的数据成员和成员函数打包在一起,对外部隐藏对象的内部实现细节,只暴露必要的接口,class本身成员默认自带的private属性就很好的表现了其封装性
    • 封装性也可以体现为某个和成员变量因为使用了static,此成员变成了静态成员变量及函数,和类本身绑定在一起,成为了形容类这个东西本身的属性
  • static标记的成员变量 – 多个实例化对象共享该成员变量的空间,是类的一部分而不是实例化对象的一部分,一般称为静态成员变量

  • static标记的成员函数(方法) – 与被static标记的成员变量一样,是类的一部分而不是实例化对象的一部分,仅可以用于静态成员变量,一般称为静态成员函数

  • 比方说以下这个例子:

    • 在这个例子中,我们定义手机这个类
    • 在苹果鼎盛时期,国产手机有一种模仿苹果的浪潮,此时手机屏幕类型普遍为:刘海屏,那我们可以认为手机类的属性一定附带有刘海屏这个特征,这和其他配置无关
class Phone
{
public:
	enum screenType
	{
		Bang_screen, //刘海屏(0)
		hole_screen, //挖孔屏(1)
	};

private:
	static screenType screentype; // 静态成员变量

public:
	// 静态成员函数,用于设置屏幕类型
	static void setScreenType(const screenType type)
	{
		screentype = type;
	}

	// 静态成员函数,用于获取屏幕类型
	static screenType getScreenType()
	{
		return screentype;
	}
};

//初始化屏幕类型
Phone::screenType Phone::screentype = Phone::Bang_screen;
//    类型       | 需要初始化的成员名 |     需要赋的值
//(老实说这里其实可以不赋初值的,当然这取决于个人习惯)

int main()
{
	Phone xiaomi;
	Phone::setScreenType(Phone::Bang_screen);
	std::cout << Phone::getScreenType() << std::endl;

	std::cout << "一段时间后,挖孔屏逐渐变成了主流" << std::endl;

	//将手机屏幕设置为挖孔屏
	Phone::setScreenType(Phone::hole_screen);
	std::cout << Phone::getScreenType() << std::endl;

	return 0;
}

请添加图片描述

  • 其实咱也能看到,调用静态成员函数的时候,全都和对象没有任何关系,所有的操作都是在类上完成的

  • 以上这个例子仅仅用来理解静态成员变量和静态成员函数与类的关系,其处于绑定状态,静态成员变量和静态成员函数属于类的一部分,对象只是共享空间,逻辑上我们认为对象继承了类的属性罢了

  • PS:在结构体中也可以遵循这套理论

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

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

相关文章

生命在于学习——Python人工智能原理(4.6)

在这里插一句话&#xff0c;我有两个好兄弟的github项目&#xff0c;感兴趣的可以去看一下&#xff0c;star一下&#xff0c;谢谢。 https://github.com/fliggyaa/fscanpoc https://github.com/R0A1NG/Botgate_bypass 四、Python的程序结构与函数 4.1 Python的分支结构 &…

C语言——链表专题

乐观学习&#xff0c;乐观生活&#xff0c;才能不断前进啊&#xff01;&#xff01;&#xff01; 我的主页&#xff1a;optimistic_chen 我的专栏&#xff1a;c语言 点击主页&#xff1a;optimistic_chen和专栏&#xff1a;c语言&#xff0c; 创作不易&#xff0c;大佬们点赞鼓…

国产操作系统上多种压缩和解压命令详解 _ 统信 _ 麒麟 _ 中科方德

原文链接&#xff1a;国产操作系统上多种压缩和解压命令详解 | 统信 | 麒麟 | 中科方德 Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇在国产操作系统上多种压缩和解压命令详解的文章。压缩和解压缩是我们在日常工作中经常需要进行的操作&#xff0c;尤其是在处理大…

49-2 内网渗透 - 使用UACME Bypass UAC

靶场准备: 1. 使用已有的 Windows 2012 虚拟机 确保你的虚拟机正在运行,并且可以正常访问。2. 添加 test 用户到管理员组(如上篇文件添加过了就跳过这一步) 具体命令如下: net localgroup administrators test /add 3. 切换用户登录 注销当前会话,并使用 test 用户登录。…

理解MySQL索引:提升查询性能的关键

一、前言 在众多数据库中&#xff0c;MySQL以其高效、稳定和跨平台的特点成为许多开发者的首选。然而&#xff0c;随着数据量的不断增加&#xff0c;查询性能可能会成为一个瓶颈。这时&#xff0c;索引&#xff08;Index&#xff09;便成为了提升查询速度的关键工具。本篇文章…

ajax请求接口不设置请求头可以请求成功,但是设置请求头之后就跨域,已解决

遇到这个问题我们不要着急找后端&#xff0c;先通过控制台看看有没有报错&#xff0c;控制台的列表是不会有这个红色报错的&#xff0c;所以我们要看下图&#xff1a; 点击这个红色&#xff0c;然后在下面会出现一些信息 很明显是这个请求头timestamp的请求头被屏蔽了&#xff…

网约车停运损失费:1、事故经过

目录 &#x1f345;点击这里查看所有博文 随着自己工作的进行&#xff0c;接触到的技术栈也越来越多。给我一个很直观的感受就是&#xff0c;某一项技术/经验在刚开始接触的时候都记得很清楚。往往过了几个月都会忘记的差不多了&#xff0c;只有经常会用到的东西才有可能真正记…

IT入门知识第八部分《云计算》(8/10)

目录 云计算&#xff1a;现代技术的新篇章 1. 云计算基础 1.1 云计算的起源和发展 云计算的早期概念 云计算的发展历程 1.2 云计算的核心特点 按需自助服务 广泛的网络访问 资源池化 快速弹性 按使用量付费 1.3 云计算的优势和挑战 成本效益 灵活性和可扩展性 维…

Matlab进阶绘图第61期—滑珠散点图

滑珠散点图也是一种在《Nature》中常见的数据图。 其功能类似于柱状图&#xff0c;但更加简洁易读。 由于Matlab中没有现成的函数绘制滑珠散点图&#xff0c;因此需要大家自行解决。 本文利用自己制作的BubbleScatter工具&#xff0c;进行滑珠散点图的绘制&#xff0c;先来看…

验证码技术 easy-captcha

依赖 <!-- easy-captcha用来生成验证码&#xff0c;由于jdk9以后&#xff0c;内置JavaScript去掉了&#xff0c;所以需要导入这个org.openjdk.nashorn --> <dependency><groupId>com.github.whvcse</groupId><artifactId>easy-captcha</arti…

Redis数据库(四):Redis数据库事务

经过前面的学习&#xff0c;我们就对于Redis数据库可以进行基本的操作&#xff0c;从这一节开始&#xff0c;我们就正式学习Redis数据库的相关知识&#xff0c;为以后工作打下坚实的基础。 目录 一、事务&#xff08;了解&#xff09; 1.1 Redis的事务概念 1.2 Redis事务…

黑马程序员——Spring框架——day09——linux初级

目录&#xff1a; 前言 什么是Linux&#xff1f;为什么要学Linux 企业用人要求个人发展需要学完Linux能干什么 1).环境搭建2).常用命令3).安装软件4).项目部署小结2.Linux简介 主流操作系统Linux发展历史Linux系统版本Linux安装 安装方式介绍安装VMware安装Linux网卡设置安装S…

我用过最好的GPT,NewspaceGPT使用心得

记住网址&#xff1a;https://newspace.ai0.cn 前言 只要你能表达明白&#xff0c;NewspaceGPT就不会让你失望。 Gpt4o预测GPT5 IT之家6月22日消息&#xff0c;在美国达特茅斯工程学院周四公布的采访中&#xff0c;OpenAI首席技术官米拉穆拉蒂被问及GPT-5是否会在明年发布&…

Beyond Compare不能比较Selinux te文件的解决方案

Beyond Compare不能比较Selinux te文件的解决方案 Beyond Compare默认不能比较te文件&#xff0c;现象如下&#xff1a; 问题原因&#xff1a; 是Beyond Compare将*.te文件识别为一个可执行文件&#xff0c;但是*.te不是一个可执行文件&#xff0c;所以打不开&#xff1b;即使…

微信群聊不见了?掌握这4个技巧轻松找回,简直太爽了

微信&#xff0c;作为国内最受欢迎的社交应用之一&#xff0c;其群聊功能极大地方便了人们的工作与生活。然而&#xff0c;随着加入的群聊数量日益增多&#xff0c;如何快速找到并管理这些群聊成为了一个难题。 幸运的是&#xff0c;微信提供了一些实用的技巧&#xff0c;帮助…

Vue原生写全选反选框

效果 场景&#xff1a;Vue全选框在头部&#xff0c;子框在v-for循环内部。 实现&#xff1a;点击全选框&#xff0c;所有子项选中&#xff0c;再次点击取消&#xff1b;子项全选中&#xff0c;全选框自动勾选&#xff0c;子项并未全选&#xff0c;全选框不勾选&#xff1b;已选…

2024全国大学生信息安全竞赛(ciscn)半决赛(华中赛区)Pwn题解

简介 前段时间赛前准备把ciscn东北赛区、华南赛区、西南赛区半决赛的题都复现完了。 可惜遇到了华东北赛区的离谱平台和离谱pwn出题人&#xff1a; 假的awdp&#xff08;直接传&#x1f40e;到靶机&#xff0c;然后连上去cat /flag.txt即可&#xff09;题型分布不合理&#…

fastadmin框架修改前端时间戳格式的时区

一、上传文件 将 moment-timezone-with-data.js 和 moment-timezone-with-data.min.js 文件上传到项目的 \public\assets\libs\moment\ 文件夹中。 二、配置中引入文件 在 \public\assets\js\require-backend.js 文件中增加所引入文件的配置: moment-timezone-with-data: …

【嵌入式Linux】i.MX6ULL GPIO 中断管理与配置函数

文章目录 GPIO 中断管理与配置函数1 GPIO中断服务号2 GPIO中断相关的寄存器配置3 具体代码分析3.1 数据结构和类型定义3.2 gpio_int_enable3.3 gpio_int_disable3.4 gpio_int_flagClear3.5 gpio_int_init3.6 gpio_init 4 完整代码 本文章结合了正点原子的 i.mx6u嵌入式Linux开发…

49、基于归一化感知器的输入向量分类(matlab)

1、基于归一化感知器的输入向量分类的原理及流程 归一化感知器是一种分类算法&#xff0c;其原理基于感知器算法&#xff0c;但是在输入向量上进行了归一化处理&#xff0c;以提高算法的性能和稳定性。 流程如下&#xff1a; 输入向量归一化&#xff1a;对每个输入向量进行归…