【c++】类和对象(二)this指针

news2024/9/23 21:26:34

Alt

🔥个人主页:Quitecoder

🔥专栏:c++笔记仓

Alt

朋友们大家好,本节内容来到类和对象第二篇,本篇文章会带领大家了解this指针

目录

  • 1.this指针
    • 1.1this指针的引出
    • 1.2this指针的特性
    • 1.3思考题
    • 1.4C语言和C++实现Stack的对比

1.this指针

1.1this指针的引出

首先我们定义一个日期类date:

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 main()
{
	Date d1, d2;
	d1.Init(2005, 6, 23);
	d2.Init(2024, 3, 25);
	d1.Print();
	d2.Print();

	return 0;
}

我们来思考这么一个问题:

Date类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,也就是说,d1和d2调用的是同一个函数,那当d1调用 Init 函数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢

在这里插入图片描述
首先思考,这里打印函数,访问的变量是哪里的?

void Print()
	{
		cout << _year << "-" << _month << "-" << _day <<endl;
	}

这里访问的是private声明下的吗?

private:
	int _year;
	int _month;
	int _day;

并不是,因为这里只是声明,并没有开辟空间,真正访问的是实例化的d1,d2

private部分声明的变量_year_month_day等,在类中只是进行了声明,实际上并没有为它们分配内存空间。**内存空间是在创建类的实例(也就是对象)**时为这些成员变量分配的。每个对象都有自己独立的一套成员变量,占用各自的内存空间

因此,当成员函数Print()通过this指针(隐式指向当前对象)访问这些成员变量时,它实际上访问的是调用这个成员函数的那个==特定对象(实例)==的成员变量。每个对象的_year_month_day都存储在各自独立的内存区域中,这些内存区域是在对象被创建时随对象一起分配的

那么我d1,d2如何找到这两个函数呢?

这里就与隐含的this指针有关了

this指针是面向对象编程语言中的一个特殊指针,它指向调用成员函数的那个对象。通过this指针,成员函数可以访问调用它的那个对象的成员变量和成员函数this指针是隐式传递给成员函数的,是成员函数的一个隐含参数

在这里插入图片描述
可以理解为,编译器处理后处理为上述的样子,调用的地方,编译器也会处理:

在这里插入图片描述
它会把调用对象当做形参进行传递

在这里插入图片描述
这里我们也能知道,为什么d1访问能打印d1,d2访问能打印d2

这个东西我们并不陌生,在前面数据结构中我们也有学过:
在这里插入图片描述

1.2this指针的特性

  1. this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递
  2. this指针的类型:类类型* const,(Date* const this)即成员函数中,不能给this指针赋值,但是this指向的内容可以被改变
    在这里插入图片描述

特点:

  1. 在形参和实参的位置,我们不能显示写出来
    在这里插入图片描述
  2. 在函数内部可以使用

在这里插入图片描述

1.3思考题

一,this指针是存在哪里的?

在这里插入图片描述
不同的数据是存储在不同的区域的,思考一下this指针是存在哪个区域的呢?

const int i = 0;
int j = 1;
cout << &i << endl;
cout << &j << endl;

c++中,const定义的变量是存储在栈中的,我们可以打印它们的地址:

在这里插入图片描述
发现是相邻的

const int i = 0;
int j = 1;
const char* p = "abcdefg";
cout << &i << endl;
cout << &j << endl;
cout << &p << endl;
cout << (void*)p << endl;

在这里插入图片描述
在C++中,变量和数据的存储位置分为几个区域,主要包括栈(Stack)、堆(Heap)、全局/静态存储区(Global/Static Area)和常量区(Constant Pool)。具体到您提供的代码示例中的变量,它们的存储位置如下:

  1. const int i = 0;

    • i是一个常量整型变量。在C++中,const修饰的局部变量默认存储在栈上,但是编译器优化可能会将其存储在程序的只读数据段中(常量区),尤其是当它被视为编译时常量时。然而,取地址操作&i表明i必须在内存中有实际的存储位置,所以它很可能位于栈上,除非进行了特殊的优化
  2. int j = 1;

    • j是一个非const局部变量,存储在栈上。栈用于存储局部变量和函数调用的上下文
  3. const char* p = "abcdefg";

    • 这里p是一个指针,指向一个字符串常量。字符串常量"abcdefg"存储在常量区(也称为字符串字面量区或只读数据段),这是因为字符串字面量在程序的整个生命周期内都不应被修改。而指针p本身(即存储字符串地址的变量)作为局部变量,存储在栈上
  • i(取决于编译器优化)和j存储在栈上。
  • 字符串常量"abcdefg"存储在常量区。
  • 指针p(存储字符串常量的地址)存储在栈上。

在上述的讲解后,我们能够推出this指针的存储位置:this是一个形参,它指向调用该成员函数的对象,this指针在成员函数调用时需要被快速访问并用于访问对象的成员,所以我们推测它存储在栈上

为了提高访问速度,某些编译器可能选择将this指针存储在某个寄存器中,尤其是在成员函数调用时。这实际上可以减少内存访问次数,从而提高程序的执行效率,寄存器是CPU内部的极小量存储器,具有非常高的数据访问速度
在这里插入图片描述

二,判断下面程序的运行结果(this能否是空指针?)

class A
{
public:
	void PrintA()
	{
		cout << "PrintA()" << endl;
	}
private:
	int _a;
};
int main()
{
	A* p = nullptr;
	p->PrintA();
	return 0;
}

在这里插入图片描述
我们发现它是可以正常运行的,我们接下来简单分析一下

尽管p被初始化为nullptr,指向A类型对象的指针p是空的,但PrintA()函数只是打印一条消息,没有访问任何对象的成员变量。这种特殊情况下,代码可运行,主要是因为成员函数的调用并没有实际依赖于this指针指向的对象实例的状态

因为PrintA()不访问对象的任何成员变量,所以这个调用在技术上不需要访问通过this指针指示的内存地址。因此,对于这种不访问任何成员变量的成员函数,通过nullptr调用可能不会导致运行时错误

简单来说,

void PrintA()
	{
		cout << "PrintA()" << endl;
	}

这串代码传递空指针并没有任何影响

接下来看下面的代码:

class A
{
public:
	void PrintA()
	{
		cout << _a << endl;
	}
private:
	int _a;
};
int main()
{
	A* p = nullptr;
	p->PrintA();
	return 0;
}

在这里插入图片描述
这串代码运行异常,因为这里访问a是通过this->_a来实现的

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

c语言实现

void StackInit(Stack* ps)
{
	assert(ps);
	ps->array = (DataType*)malloc(sizeof(DataType) * 3);
	if (NULL == ps->array)
	{
		assert(0);
		return;
	}
	ps->capacity = 3;
	ps->size = 0;
}
void StackDestroy(Stack* ps)
{
	assert(ps);
	if (ps->array)
	{
		free(ps->array);
		ps->array = NULL;
		ps->capacity = 0;
		ps->size = 0;
	}
}
void CheckCapacity(Stack* ps)
{
	if (ps->size == ps->capacity)
	{
		int newcapacity = ps->capacity * 2;
		DataType* temp = (DataType*)realloc(ps->array,
			newcapacity * sizeof(DataType));
		if (temp == NULL)
		{
			perror("realloc申请空间失败!!!");
			return;
		}
		ps->array = temp;
		ps->capacity = newcapacity;
	}
}
void StackPush(Stack* ps, DataType data)
{
	assert(ps);
	CheckCapacity(ps);
	ps->array[ps->size] = data;
	ps->size++;
}
int StackEmpty(Stack* ps)
{
	assert(ps);
	return 0 == ps->size;
}
void StackPop(Stack* ps)
{
	if (StackEmpty(ps))
		return;
	ps->size--;
}
DataType StackTop(Stack* ps)
{
	assert(!StackEmpty(ps));
	return ps->array[ps->size - 1];
}
int StackSize(Stack* ps)
{
	assert(ps);
	return ps->size;
}
int main()
{
	Stack s;
	StackInit(&s);
	StackPush(&s, 1);
	StackPush(&s, 2);
	StackPush(&s, 3);
	StackPush(&s, 4);
	printf("%d\n", StackTop(&s));
	printf("%d\n", StackSize(&s));
	StackPop(&s);
	StackPop(&s);
	printf("%d\n", StackTop(&s));
	printf("%d\n", StackSize(&s));
	StackDestroy(&s);
	return 0;
}

在用C语言实现时,Stack相关操作函数有以下共性

  • 每个函数的第一个参数都是Stack*
  • 函数中必须要对第一个参数检测,因为该参数可能会为NULL
  • 函数中都是通过Stack*参数操作栈的
  • 调用时必须传递Stack结构体变量的地址

结构体中只能定义存放数据的结构,操作数据的方法不能放在结构体中,即数据和操作数据的方式是分离开的

c++实现

typedef struct Stack
{
	DataType* array;
	int capacity;
	int size;
}Stack;
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语言中需用用户自己维护

感谢大家阅读!!!后续给大家带来析构函数和构造函数有关内容!

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

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

相关文章

RWTH-PHOENIX Weather数据集模型说明和下载

RWTH-PHOENIX Weather 2014 T数据集说明: 德国公共电视台PHOENIX在三年内(2009 年至 2011 年) 录制了配有手语翻译的每日新闻和天气预报节目,并使用注释符号转录了 386 个版本的天气预报。 此外,我们使用自动语音识别和手动清理来转录原始德语语音。因此,该语料库允许训练…

近线数仓优化改造

近线数仓优化改造 1. 背景2. 优化3. 改造3.1. 重构3.2. 优化 1. 背景 大概就是有那么一个数仓&#xff0c;然后简略结构如下&#xff1a; #mermaid-svg-PVoUzuQhj2BK7Qge {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid…

【C语言】动态内存管理及其常见错误

文章目录 1、前言&#xff1a;为什么要有动态内存分布2、三种动态内存的创建方式及其释放2.1 malloc2.2 calloc2.3 ralloc2.4 free 3、常⻅的动态内存的错误3.1 对NULL指针的解引用操作3.2 对动态开辟空间的越界访问3.3 对非动态开辟内存使用free释放3.4 使⽤free释放⼀块动态开…

C++动态内存管理:new/delete与malloc/free的对比

在C中&#xff0c;动态内存管理是一个至关重要的概念。它允许我们在程序运行时根据需要动态地分配和释放内存&#xff0c;为对象创建和销毁提供了灵活性。在C中&#xff0c;我们通常会用到两对工具&#xff1a;new/delete 和 malloc/free。虽然它们都能够完成类似的任务&#x…

2月线上速溶咖啡行业数据分析:“减肥咖啡”引领电商新潮流

随着生活节奏的加快&#xff0c;速溶咖啡因其便捷性受到广大消费者的青睐。不过&#xff0c;在如今世界咖啡市场激烈竞争的情况下&#xff0c;中国速溶咖啡市场也受到影响&#xff0c;增速有所放缓。 根据鲸参谋电商数据平台显示&#xff0c;2月线上综合电商&#xff08;京东天…

003_vector_conventions_in_MATLA中的向量约定

MATLAB中的向量约定 1. 前言 MATLAB是一种用于数值计算和数据可视化的高级编程语言。以前&#xff0c;都不好意思说它是编程语言&#xff0c;它实际上只是一个脚本工具&#xff0c;配套了一堆工具箱。比如Simulink&#xff0c;可以开展非常复杂的仿真&#xff0c;还能编译到实…

海外媒体发稿:出口贸易媒体发稿7个秘籍揭晓-华媒舍

出口贸易是许多国家经济增长的关键驱动力之一。不仅可以加快国家的发展步伐&#xff0c;还能为企业创造巨大的商机。如何能够在出口贸易中取得成功&#xff0c;如何能够引起媒体的关注&#xff0c;成为企业广告和宣传的焦点&#xff0c;是许多出口企业面临的挑战。本文将揭示出…

【LeetCode热题100】108. 将有序数组转换为二叉搜索树(二叉树)

一.题目要求 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 平衡二叉搜索树。 二.题目难度 简单 三.输入样例 示例 1&#xff1a; 输入&#xff1a;nums [-10,-3,0,5,9] 输出&#xff1a;[0,-3,9,-10,null,5] 解释&#x…

【Java程序设计】【C00367】基于(JavaWeb)Springboot的粮仓管理系统(有论文)

TOC 博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;已经做了六年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;博客中有上百套程序可供参考&#xff0c;欢迎共同交流学习。 项目简介 项目获取 &#x1f345;文末点击卡片…

JVM——字符串常量池

在Java程序中String类的使用几乎无处不在&#xff0c;String类代表字符串&#xff0c;字符串对象可以说是Java程序中使用最多的对象了。首先&#xff0c;在Java中创建大量对象是非常耗费时间的。其次&#xff0c;在程序中又经常使用相同的字符串对象&#xff0c;如果每次都去重…

企业计算机服务器中了locked勒索病毒怎么处理?Locked勒索病毒解密流程

在网络技术不断发展应用过程中&#xff0c;越来越多的企业利用网络开展各项工作业务&#xff0c;网络为企业的生产运营提供了极大便利&#xff0c;但网络威胁手段也在不断增加&#xff0c;为企业的数据安全带来严重威胁。近日&#xff0c;新一波的网络勒索病毒比较猖獗&#xf…

云电脑火爆出圈,如何选择和使用?--腾讯云、ToDesk云电脑、青椒云使用评测和攻略

前言&#xff1a; Hello大家好&#xff0c;我是Dream。在当下&#xff0c;科技的飞速发展已经深入影响着我们的日常生活&#xff0c;特别是随着物联网的兴起和5G网络的普及&#xff0c;云计算作为一个重要的技术概念也逐渐走进了我们的视野。云计算早已不再是一个陌生的名词&am…

FANUC机器人零点标定的基本步骤(出厂数据)

FANUC机器人零点标定的基本步骤(出厂数据) FANUC 零点数据存在问题的机器人通常会出现以下几种报警: (1)SRVO-062报警 - 脉冲编码器数据丢失,机器人完全不能动,具体消除方法可参考以下链接中的内容: FANUC机器人SRVO-062报警原因分析及处理对策 (2)SRVO-075报警 -…

qrcode插件-生成二维码

安装 yarn add qrcodejs2 --save npm install qrcodejs2 --save 使用 <template><div><div id"qrcodeImg"></div><!-- 创建一个div&#xff0c;并设置id --></div> </template> <script> import QRCode from q…

9.串口通信

串口基本认识 串行接口简称串口&#xff0c;也称串行通信接口或串行通讯接口&#xff08;通常指COM接口&#xff09;&#xff0c;是采用串行通信方 式的扩展接口。串行接口&#xff08;Serial Interface&#xff09;是指数据一位一位地顺序传送。其特点是通信线路简 单&#x…

qt材料库---最新版(使用QsqlDatabase、QJsonObject)

将每种材料的参数设置保存为json,并将整个json语句放到数据库中,同时显示到列表中。 选中某行时,将参数反馈到控件上 源码 Material.h #pragma once #include "QtWindow.h" #include "ui_Widget.h" #include <QWidget> #include <QObject>…

Jetson AGX ORIN 配置 FGVC-PIM 神经网络(包含 arm64 下面 torch 和 torchvision 配置内容)

Jetson AGX ORIN 配置 FGVC-PIM 神经网络 文章目录 Jetson AGX ORIN 配置 FGVC-PIM 神经网络配置 ORIN 环境创建 FGVC-PIM 虚拟环境安装 PyTorch安装 torchvision安装其他依赖包 配置 ORIN 环境 首先先配置 ORIN 的环境&#xff0c;可以参考这个链接&#xff1a; Jetson AGX …

如何为企业策划一场XR虚拟直播?

活动年年办&#xff0c;都是老一套&#xff0c;想玩点新花样&#xff1f; 预算有限&#xff0c;但还是想把活动办的逼格高一点&#xff1f; 想通过活动&#xff0c;让更多的人知道自己企业的品牌&#xff1f; 随着AIGC技术的不断演变&#xff0c;企业活动的形式和内容也在不…

深入了解 Vue 3:性能与可用性的巨大提升

摘要&#xff1a;本文深入探讨了 Vue 3 相对于 Vue 2 在性能和可用性方面的重大改进&#xff0c;特别关注了虚拟 DOM 模块的重构&#xff08;静态提升&#xff09;、基于 Proxy 的响应式对象、事件缓存、更好的 Tree Shaking 支持、TypeScript 和 Monorepo 代码组织&#xff0c…

Java两地经纬度通过高德api获取两地距离(公里)

代码如下&#xff1a; String startLongitude entity.getLONGITUDE(); // 起点&#xff08;当前位置&#xff09;经度String startLatitude entity.getLATITUDE(); // 起点纬度String endLongitude entity.getLO(); // 终点经度String endLatitude entity.getLA(); …