C++:类与对象(上)

news2024/11/16 3:24:48

C++:类与对象(上)

    • 类的引入
    • 类的定义
    • 访问限定符
    • 类域
    • 实例化
    • 对象模型
    • this指针


类的引入

C++的类是基于C语言的结构体优化出来的,那我们先来看一看C++对结构体有哪些优化点。

C语言与C++的结构体的类型名称略有区别,我们看一个案例:

//C语言的结构体
struct ListNode1
{
	int val;
	struct ListNode1* next;
};

//C++的结构体
struct ListNode2
{
	int val;
	ListNode2* next;
};

int main()
{
	struct ListNode1 L1;//C语言的结构体创建变量
	ListNode2 L2;//C++的结构体创建变量
}

一开始我们定义了两个结构体:ListNode1ListNode2,在mian函数中我们分别对两个结构体创建了变量。
在C语言的结构体创建的变量时,需要用struct ListNode1作为类型来创建。
而在C++的结构体创建变量时,只需要ListNode2作为类型来创建。

也就是说,在C语言中结构体创建变量需要加上struct关键字,而C++中优化了,可以无需这个关键字。

所以我们在结构体内部定义的next节点的指针,也分别是struct ListNode1*ListNode2*

C语言中结构体只能用于定义变量,但是在C++中,结构体内不仅可以定义变量,还可以定义函数

struct stack
{
	void Init()
	{
		cout << "初始化栈" << endl;
	}
	
	void Push(int x)
	{
		cout << x << "被压栈了" << endl;
	}
	
	void Destroy()
	{
		cout << "销毁栈" << endl;
	}
	
	int stackSize()
	{
		return size;
	}
	
	int* a;
	int size;
	int capacity;
};

上述代码中,我们定义了一个stack结构体,其内部有asizecapacity三个基本变量,这是在C语言结构体范围内的。但是我们还额外定义了几个函数在其内部InitPushDestroy以及stackSize。我们可以通过结构体来调用某一个函数。

那么要如何调用函数?
其实只要把函数当作一个普通的变量,用.操作符访问即可。
在这里插入图片描述
这样我们就正常访问到了结构体内部的函数。

我们看到结构体内的一个函数:

int stackSize()
{
	return size;
}

这个函数里面没有size这个形参,为什么可以直接使用size?
在同一个结构体内部的变量,是可以直接被结构体内的函数访问到的。因为size存在于结构体中,所以结构体内的函数可以访问到size

但是为了更好的区别C语言的结构体与C++的结构体,C++更喜欢用一种叫做类的结构来替代结构体。


类的定义

class关键字是定义类的关键字,在C++中,直接使用class代替struct即可,而我上方说的所有C++对结构体的优化,在类中也使用。

上述结构体可以定义为以下的类:

class stack
{
	void Init()
	{
		cout << "初始化栈" << endl;
	}
	
	void Push(int x)
	{
		cout << x << "被压栈了" << endl;
	}
	
	void Destroy()
	{
		cout << "销毁栈" << endl;
	}
	
	int stackSize()
	{
		return size;
	}
	
	int* a;
	int size;
	int capacity;
};

其中函数部分叫做成员函数或者方法,变量部分叫做类的属性或者成员变量

但是这样定义类会有一个问题:
在这里插入图片描述
为什么我们定义的类,无法访问到其内部的函数了???

这是因为对于类来说,其所有成员会被分为公有和私有,接下来我们看看什么是公有私有,以及如何转换:


访问限定符

在这里插入图片描述

  1. public修饰的成员在类外可以直接被访问
  2. protectedprivate修饰的成员在类外不能直接被访问(这方面protectedprivate是类似的)
  3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
  4. 如果后面没有访问限定符,作用域就到} 即类结束。
  5. class的默认访问权限为privatestructpublic(因为struct要兼容C语言)

首先看到第1,2点: public修饰的成员,在类外部可以直接访问,而 protectedprivate修饰的成员在类外不能直接被访问。
再看到第五点: class的默认访问权限为privatestructpublic

也就是说,class定义的类,默认外部无法访问到其内部的成员,所以当我们把struct转为 class,外部访问函数就报错了。

所以以上代码要改为:

class stack
{
public:
	void Init()
	{
		cout << "初始化栈" << endl;
	}

	void Push(int x)
	{
		cout << x << "被压栈了" << endl;
	}

	void Destroy()
	{
		cout << "销毁栈" << endl;
	}

	int stackSize()
	{
		return size;
	}

	int* a;
	int size;
	int capacity;
};

这样整个类都可以被外部访问了,因为所有的成员都在public下方。
但是我们很多时候不希望别人修改我们的数据,只希望别人调用函数。

所以我们可以把成员变量改为私有:

class stack
{
public:
	void Init()
	{
		cout << "初始化栈" << endl;
	}

	void Push(int x)
	{
		cout << x << "被压栈了" << endl;
	}

	void Destroy()
	{
		cout << "销毁栈" << endl;
	}

	int stackSize()
	{
		return size;
	}
private:
	int* a;
	int size;
	int capacity;
};

对于protect,这里不做详解,其与private的区别要在后续才能体现。


类域

类是单独享有一个域的,所以类与类之间可以有同名函数与变量,如下:

class stack
{
public:
	void Init()
	{
		cout << "初始化栈" << endl;
	}

	void Push(int x)
	{
		cout << x << "被压栈了" << endl;
	}

	void Destroy()
	{
		cout << "销毁栈" << endl;
	}

	int stackSize()
	{
		return size;
	}
private:
	int* a;
	int size;
	int capacity;
};

class List
{
public:
	void Init()
	{
		cout << "初始化顺序表" << endl;
	}

	void Push(int x)
	{
		cout << x << "顺序表尾插" << endl;
	}

	void Destroy()
	{
		cout << "销毁顺序表" << endl;
	}

private:
	int* a;
	int size;
};

以上代码中,定义了一个顺序表List,一个栈stack,两者都有InitPushDestroyasize。但是两者并不冲突,因为在不同的类域中。


实例化

类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它。

可以理解为,类就是一个建筑图纸,其规定了一个建筑有哪些房间,房间大小,尺寸等等。但是图纸是不可以住人的,也就是说还不是一个可以使用的屋子,此时就需要实例化,根据图纸把屋子建出来。
在这里插入图片描述
比如我们上述的栈的类,将其实例化就是:

int main()
{
	stack s1;
	stack s2;
	stack s3;
	stack s4;

	return 0;
}

这里我们进行了四次实例化,s1s2s3s4是四个不同的实例,不过我们一般将其称为对象。这四个对象就相当于根据图纸造出来的屋子,它们的结构完全一致,只是可能经过使用后数据会不同。可以理解为不同装修方式会对房屋的内部造成不同的影响,但是房屋的结构依然是一致的。


对象模型

我们先尝试检测一个对象的大小:

class stack
{
public:
	void Init()
	{
		cout << "初始化栈" << endl;
	}

	void Push(int x)
	{
		cout << x << "被压栈了" << endl;
	}

	void Destroy()
	{
		cout << "销毁栈" << endl;
	}

	int stackSize()
	{
		return size;
	}
private:
	int* a;
	int size;
	int capacity;
};

int main()
{
	cout << sizeof(stack) << endl;//输出16
	return 0;
}

如果你结构体的位段学的好的话,你会发现,其实结构体中只有asizecapacity三个成员,大小也是16个字节。
也就是说:类中的函数没有占用类的空间

其机制为:类的成员变量放在对象本身的空间中,而函数会被放在一个公共代码段,因为函数是一致的。
在这里插入图片描述
所以计算类的大小时,只需要通过结构体的位段,计算成员变量的大小。

除此之外:如果一个类是空类,或者只有成员函数,那么编译器依然会为其分配一个字节的空间


this指针

讲清楚了对象模型后,我们来想一个问题:既然大家都是用的同一个函数,那么这个被调用的函数怎么知道是谁调用的?

比如这样:

class myclass
{
public:
	void Init(int x, int y, int z)
	{
		_x = x;
		_y = y;
		_z = z;
	}

	void Add()
	{
		cout << _x + _y + _z << endl;
	}

private:
	int _x;
	int _y;
	int _z;
};

int main()
{
	myclass c1;
	myclass c2;

	c1.Init(1, 2, 3);
	c2.Init(4, 5, 6);

	c1.Add();
	c2.Add();

	return 0;
}

创建了两个myclass类的对象c1c2c1初始化三个成员为1,2,3;c2初始化三个成员为4,5,6。
调用Add函数时,对于Add函数来说,它是如何知道要完成1+2+3还是4+5+6?

这就涉及到了this指针。
C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。

也就是说,我们在调用类的函数时,函数会偷偷传递一个参数this指针,它指向了成员的地址,这样就可以知道是谁调用了这个函数。

this指针有以下特性:

  1. this指针被const修饰了,函数内部是不允许修改this指针的指向的。
  2. this指针在函数内部是允许使用的

比如我们可以在类中添加一个函数:

void tellMeThis()
{
	cout << this << endl;
}

这个函数会输出一个隐藏的变量this,我们可以访问this指针。

但是如果我们想在函数内部修改this:

void changeThis()
{
	this = NULL;
}

这就是非法的了。


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

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

相关文章

安卓Spinner文字看不清

Holo主题安卓13的Spinner文字看不清&#xff0c;明明已经解决了&#xff0c;又忘记了。 spinner.setOnItemSelectedListener(new Spinner.OnItemSelectedListener() {public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {TextView textV…

Rust 程序设计语言学习——基础语法

Rust 语言是一种高效、可靠的通用高级语言。其高效不仅限于开发效率&#xff0c;它的执行效率也是令人称赞的&#xff0c;是一种少有的兼顾开发效率和执行效率的语言。 Rust 语言由 Mozilla 开发&#xff0c;最早发布于 2014 年 9 月。Rust 的编译器是在 MIT License 和 Apach…

【好用的AI工具】推荐测试人在用的Kimi Chat

一、功能介绍 发网址链接文章解析PDF文件分析&#xff0c;可以整理分析文章丢简历、给出面试问题聊天等 Kimi Chat 二、对于测试人带来的帮助 2.1 面试问题总结 问题一&#xff1a;Session和Cookie的区别 seeion 和 cookie 是两种不同的数据存储机制&#xff0c;它们在Web开…

【C语言深度剖析——第四节(关键字4)】《C语言深度解剖》+蛋哥分析+个人理解

追求本质&#xff0c;不断进步 本文由睡觉待开机原创&#xff0c;转载请注明出处。 本内容在csdn网站首发 欢迎各位点赞—评论—收藏 如果存在不足之处请评论留言&#xff0c;共同进步&#xff01; 这里写目录标题 一、空间的申请1.变量定义1.1变量定义的概念&#xff1a;1.2变…

基本控件(一)

目录 控件的定义&#xff1a;使用控件的好处&#xff1a; setupUiwidget.hwidget.cppmain.cpp 控件的定义&#xff1a; QT控件是在QT框架下实现的一组用户界面元素&#xff0c;包括按钮、标签、输入框、列表框、滚动条、菜单等等&#xff0c;它们用于快速构建各种应用程序的图…

工业相机与镜头参数及选型

文章目录 1、相机成像系统模型1.1 视场1.2 成像简化模型 2、工业相机参数2.1 分辨率2.2 靶面尺寸2.3 像元尺寸2.4 帧率/行频2.5 像素深度2.6 动态范围2.7 信噪比2.8 曝光时间2.9 相机接口 3、工业镜头参数3.1 焦距3.2 光圈3.3 景深3.4 镜头分辨率3.5 工作距离&#xff08;Worki…

大模型笔记【3】 gem5 运行模型框架LLama

一 LLama.cpp LLama.cpp 支持x86&#xff0c;arm&#xff0c;gpu的编译。 1. github 下载llama.cpp https://github.com/ggerganov/llama.cpp.git 2. gem5支持arm架构比较好&#xff0c;所以我们使用编译LLama.cpp。 以下是我对Makefile的修改 开始编译&#xff1a; make UNAME…

详解SpringCloud微服务技术栈:DockerCompose部署微服务集群

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位大四、研0学生&#xff0c;正在努力准备大四暑假的实习 &#x1f30c;上期文章&#xff1a;详解SpringCloud微服务技术栈&#xff1a;Gateway网关&#xff08;断言、过滤器、跨域问题&#xff09; &#x1f4da;订阅专栏&…

【.NET Core】多线程之线程池(ThreadPool)详解(二)

【.NET Core】多线程之线程池&#xff08;ThreadPool&#xff09;详解&#xff08;二&#xff09; 在上一篇《【.NET Core】多线程之线程池&#xff08;ThreadPool&#xff09;详解&#xff08;一&#xff09;》中我们详细讲解了&#xff0c;线程池概念&#xff0c;如何应用及…

测试C#调用OpenCvSharp和ViewFaceCore从摄像头中识别人脸

学习了基于OpenCvSharp获取摄像头数据&#xff0c;同时学习了基于ViewFaceCore的人脸识别用法&#xff0c;将这两者结合即是从摄像头中识别人脸。本文测试测试C#调用OpenCvSharp和ViewFaceCore从摄像头中识别人脸&#xff0c;并进行人脸红框标记。   新建Winform项目&#xf…

白盒测试?看这一篇就够了

白盒测试&#xff1f;看这一篇就够了 什么是白盒测试&#xff1a;白盒测试流程白盒测试技术白色测试分 2 个步骤进行白盒测试的特点白盒测试的优点白盒测试的缺点 五星上将麦克阿瑟曾经说过“在白盒测试面前&#xff0c;黑盒测试就是弟弟“ 什么是白盒测试&#xff1a; 白盒测…

10篇深度学习时间序列预测综述汇总!含金融、医疗、气象等领域应用!

在当今数据驱动的时代&#xff0c;时间序列预测在金融、医疗和气象等领域发挥着关键作用&#xff0c;而深度学习作为人工智能领域的重要分支&#xff0c;为解决时间序列预测问题提供了强大的工具。 为了帮助大家深入理解深度学习时间序列预测&#xff0c;这里整理了10篇深度学习…

springboot配置项动态刷新

文章目录 一&#xff0c;序言二&#xff0c;准备工作1. pom.xml引入组件2. 配置文件示例 三&#xff0c;自定义配置项动态刷新编码实现1. 定义自定义配置项对象2. 添加注解实现启动时自动注入3. 实现yml文件监听以及文件变化处理 四&#xff0c;yaml文件转换为java对象1. 无法使…

Oracle认证 | 甲骨文Oracle认证含金量高吗?

“考证”在各行各业中一直都是热度不减的话题&#xff0c;IT领域也不例外。 对于在校学生来说&#xff0c;并没有太多实践经验&#xff0c;证书在一定程度上就是找工作时的“敲门砖”&#xff0c;多张证书多条路&#xff1b; 对于职场人士来说&#xff0c;证书在升职加薪、跳…

基于springboot+vue的网上购物商城(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目背景…

【设计模式】张一鸣笔记:责任链接模式怎么用?

我将通过一个贴近现实的故事——请假审批流程&#xff0c;带你了解和掌握责任链模式。 什么是责任链模式&#xff1f; 责任链模式是一种行为设计模式&#xff0c;它让你可以避免将请求的发送者与接收者耦合在一起&#xff0c;让多个对象都有处理请求的机会将这个对象连成一条…

【架构师专题】架构师如何做好业务架构?

作为一个优秀的架构师&#xff0c;必须要先训练自己的业务架构技能&#xff0c;但是要训练之前&#xff0c;一定要先理解“业务架构”的定义。 业务架构 那么什么是业务架构呢&#xff1f;这里我给大家梳理了一个比较准确的定义。 “业务架构”是一个企业或组织在运营过程中…

大模型日报-20240122

清华、小米、华为、 vivo、理想等多机构联合综述&#xff0c;首提个人LLM智能体、划分5级智能水平 https://mp.weixin.qq.com/s/JYB4BzsXhWF8pEUUkvn_GQ 想必这些唤醒词中至少有一个曾被你的嘴发出并成功呼唤出了一个能给你导航、讲笑话、添加日程、设置闹钟、拨打电话的智能个…

gradle构建spring-framework源码

5.3.22版本构建 通过启动的jvm参数配置代理下载 Could not download jruby-stdlib-9.2.20.1.jar (org.jruby:jruby-stdlib:9.2.20.1) Could not get resource https://repo.maven.apache.org/maven2/org/jruby/jruby-stdlib/9.2.20.1/jruby-stdlib-9.2.20.1.jar. Could not GE…

Vue3组件库开发 之Button(2) 未完待续

Vue3组件库开发 之Button(1) 中新建项目&#xff0c;但未安装成功ESLINT 安装ESLINT npm install eslint vite-plugin-eslint --save-dev 安装eslint后&#xff0c;组件文件出现错误提示 添加第三方macros &#xff0c;虽然不是官网但很多开发者都是vue3开发人员 安装macros…