C++从入门到精通——this指针

this指针

  • 前言
  • 一、this指针的引出
    • 问题
  • 二、this指针的特性
  • 三、例题
    • 什么时候会出现编译报错
    • 什么时候会出现运行崩溃
    • this指针存在哪里
    • this指针可以为空吗
  • 四、C语言和C++实现Stack的对比
    • C语言实现
    • C++实现


前言

this指针是一个特殊的指针,在C++类的成员函数中使用。它指向调用该成员函数的对象的地址。通过使用this指针,成员函数可以访问和修改调用它的对象的属性和其他成员函数。这种机制使得成员函数能够识别和操作其所属的对象,从而实现了面向对象编程中的封装性和数据隐藏。


一、this指针的引出

this指针是C++中的一个特殊指针,它指向当前对象。它的引入主要是为了解决成员函数与成员变量同名的问题。

在一个类中,成员函数可以访问类的成员变量。当类的成员变量与成员函数的参数同名时,如果没有使用this指针,编译器无法区分两者。因此,this指针的引入使得编译器能够准确地识别成员变量与成员函数的参数。

this指针可以在非静态成员函数中使用,它指向调用该函数的对象,可以通过this指针访问对象的成员变量和成员函数。

this指针的使用场景主要有以下几种:

  • 在类的成员函数中,如果成员变量与成员函数的参数同名,可以使用this指针来明确指出要访问的是成员变量。
  • 在类的成员函数中,如果需要返回当前对象本身,可以使用return *this;
  • 在类的成员函数中,如果需要在函数中访问当前对象的地址,可以使用this指针来获取。

总结来说,this指针的引入解决了成员函数与成员变量同名的问题,同时也提供了一种简便的方式来访问当前对象的成员变量和成员函数。

问题

我们先来定义一个日期类 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(2022, 1, 11);d2.Init(2022, 1, 12);d1.Print();d2.Print();return 0;
}

对于上述类,有这样的一个问题:

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

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

二、this指针的特性

  1. this指针的类型:类类型* const,即成员函数中,不能给this指针赋值。
  2. 只能在“成员函数”的内部使用
  3. this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
  4. this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递
  5. 禁止在静态成员函数中使用:静态成员函数不属于任何对象,因此不能使用this指针。
  6. 允许链式调用:this指针的存在允许成员函数进行链式调用,即返回*this指针。
  7. 可以修改成员变量:使用this指针可以访问和修改当前对象的成员变量。
  8. 可以调用其他成员函数:使用this指针可以调用当前对象的其他成员函数。
  9. 可以用于比较和判断是否为同一对象:使用this指针可以比较两个对象是否为同一个对象。

在这里插入图片描述

三、例题

// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:void Print(){cout << "Print()" << endl;}
private:int _a;
};
int main()
{A* p = nullptr;p->Print();return 0;
}

在这个代码中,首先定义了一个类 A,其中有一个公有的成员函数 Print() 和一个私有的成员变量 _a

然后在主函数 main 中,定义了一个 A 类型的指针 p,并将其初始化为 nullptr。接下来,通过 p 指针调用 Print() 函数。
由于 p 是一个空指针,正常来说试图通过空指针调用函数会导致运行时错误,但是本题并没有对指针进行解引用调用,而是直接使用cout函数,所以会正常运行。


// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:void PrintA(){cout << _a << endl;}
private:int _a;
};
int main()
{A* p = nullptr;p->PrintA();return 0;
}

在这个代码中,首先定义了一个类 A,其中有一个公有的成员函数 Print() 和一个私有的成员变量 _a

然后在主函数 main 中,定义了一个 A 类型的指针 p,并将其初始化为 nullptr。接下来,通过 p 指针调用 Print() 函数。
由于 p 是一个空指针,试图通过空指针调用函数会导致运行时错误,本题是对p指针解引用调用_a,所以会出现运行崩溃(即this->_a)

什么时候会出现编译报错

编译报错通常在编程过程中出现,以下是一些常见的情况:

  1. 语法错误:如果代码中包含了错误的语法,编译器将无法解析代码并报错。例如,缺少括号、缺少分号、错误的变量命名等。

  2. 类型错误:如果代码中使用了错误的类型或进行了不兼容的类型转换,编译器将报错。例如,将字符串赋值给整数类型的变量、使用未声明的变量等。

  3. 缺少依赖库:如果代码中使用了某个依赖库,但没有将其正确导入或链接到项目中,编译器将无法找到该库并报错。

  4. 重复定义:如果代码中定义了重复的变量、函数或类型等,编译器将报错。

  5. 系统限制:有时编译器会实施一些限制,例如最大堆栈大小、代码行数限制等。如果代码超过了这些限制,编译器将报错。

当编译报错时,通常会提供详细的错误信息,其中包含了错误的位置和具体原因,开发人员可以根据这些信息来定位和修复错误。

什么时候会出现运行崩溃

运行崩溃是指在程序运行过程中突然停止或无响应的情况。崩溃可能出现在各种软件和硬件系统中,以下列举了一些常见的运行崩溃的情况:

  1. 程序错误:程序中存在错误或漏洞,导致程序运行时崩溃。这可能是由于编程错误、内存泄漏、资源耗尽等引起的。

  2. 内存问题:程序运行时需要占用大量内存,但系统资源不足,导致程序崩溃。这可能是由于内存泄漏、内存溢出、过多的进程占用内存等引起的。

  3. 硬件故障:硬件设备出现故障,导致程序无法正常运行或崩溃。这可能是由于硬盘故障、电源故障、内存损坏等引起的。

  4. 操作系统错误:操作系统出现错误,导致程序无法正常运行或崩溃。这可能是由于操作系统错误、驱动程序冲突、系统文件损坏等引起的。

  5. 网络问题:程序依赖网络连接进行通信,但网络出现故障或断开,导致程序无法正常运行或崩溃。

总而言之,运行崩溃可能由多种原因引起,包括程序错误、内存问题、硬件故障、操作系统错误、网络问题等。对于开发者来说,重要的是通过调试和测试找出并修复这些问题,以确保程序能够稳定运行。

this指针存在哪里

this指针是在C++类中的一个特殊指针,它指向当前对象的地址。在类的成员函数中,可以使用this指针来访问当前对象的成员变量和成员函数。在C++中,每个非静态成员函数都隐含地包含一个this指针。
在这里插入图片描述

this是个形参,存放在栈区中,或叫ecx寄存器,上述图片可以直接展现编译器将d1的地址存放到寄存器中

this指针可以为空吗

this指针可以为空。在C++中,this指针指向当前对象的地址,如果对象不存在,即为空,this指针也将为空。在访问对象的成员函数时,需要先判断this指针是否为空,以避免访问空指针错误。

这个问题的具体示例在上述的题目,我们给this传入了一个空指针,我们不对this指针进行解引用,程序是正常运行的,我们一旦解引用程序便会报错。

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

C语言实现

typedef int DataType;
typedef struct Stack
{DataType* array;int capacity;int size;
}Stack;
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 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.mzph.cn/news/799460.shtml

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

相关文章

Java笔试总结

. /etc/resolv.conf的用途是 A 邮件服务的设置文件 B DHCP的设置文件 C DNS解析的设置文件 D 网络路由的设置文件 答案: C 能够PING通同网段的节点&#xff0c;但却如PING不通其他网段的所有节点的最可能的原因() A 本机网关设置错误 B 本机没有正确设置DNS C 对方运行的是不…

webpack环境配置分类结合vue使用

文件目录结构 按照目录结构创建好文件 控制台执行: npm install /config/webpack.common.jsconst path require(path) const {merge} require(webpack-merge) const {CleanWebpackPlugin} require(clean-webpack-plugin) const { VueLoaderPlugin } require(vue-loader); c…

第十二届蓝桥杯大赛软件赛省赛C/C++大学B组

第十二届蓝桥杯大赛软件赛省赛C/C 大学 B 组 文章目录 第十二届蓝桥杯大赛软件赛省赛C/C 大学 B 组1、空间2、卡片3、直线4、货物摆放5、路径6、时间显示7、砝码称重8、杨辉三角形9、双向排序10、括号序列 1、空间 1MB 1024KB 1KB 1024byte 1byte8bit // cout<<"2…

nodejs详细安装步骤和npm配置

1、Node.js简介 Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时。Node.js 使用高效、轻量级的事件驱动、非阻塞 I/O 模型。它的包生态系统&#xff0c;npm&#xff0c;是目前世界上最大的开源库生态系统。 2、下载Node.js 官方地址&#xff1a;https://nodejs.org/…

STM32CubeMX+MDK通过I2S接口进行音频输入输出(全双工读写一个DMA回调)

一、前言 目前有一个关于通过STM32F411CEUx的I2S总线接口控制SSS1700芯片进行音频输入输出的研究。 SSS1700 是具有片上振荡器的 3S 高度集成的USB音频控制器芯片 。 SSS1700 功能支持96 KHz 24 位采样率&#xff0c;带外部音频编解码器&#xff08;24 位/96KHz I2S 输入和输出…

RabbitMQ的交换机与队列

一、流程 首先先介绍一个简单的一个消息推送到接收的流程&#xff0c;提供一个简单的图 黄色的圈圈就是我们的消息推送服务&#xff0c;将消息推送到 中间方框里面也就是 rabbitMq的服务器&#xff0c;然后经过服务器里面的交换机、队列等各种关系&#xff08;后面会详细讲&am…

腾讯云4核8G服务器12M带宽646元1年零3个月,4C8G使用场景说明

腾讯云4核8G服务器多少钱&#xff1f;腾讯云4核8G轻量应用服务器12M带宽租用价格646元15个月&#xff0c;活动页面 txybk.com/go/txy 活动链接打开如下图所示&#xff1a; 腾讯云4核8G服务器优惠价格 这台4核8G服务器是轻量应用服务器&#xff0c;详细配置为&#xff1a;轻量4核…

【ARMv7-M】| 01——阅读笔记 | 简介|应用程序级编程和内存模型

系列文章目录 【ARMv7-M】| 01——阅读笔记 | 简介|应用程序级编程和内存模型 失败了也挺可爱&#xff0c;成功了就超帅。 文章目录 前言1、简介2、应用程序级编程模型2.1 编程模式和访问等级2.2 数据类型和运算操作2.3 寄存器和执行状态1.2.4 异常和中断1.2.5 浮点单元寄存器…

【JAVASE】带你了解面向对象三大特性之一(多态)

✅作者简介&#xff1a;大家好&#xff0c;我是橘橙黄又青&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;再无B&#xff5e;U&#xff5e;G-CSDN博客 1.多态 1.1 多态的概念 多态的概念&#xff1a;通俗来说&#…

鸿蒙学习记录

问题小测记录 总结链接&#xff1a;小测总结 学习笔记&#xff1a;鸿蒙开发学习记录 1、 main_pages.json存放页面page路径配置信息。 2、在stage模型中&#xff0c;下列配置文件属于AppScope文件夹的是&#xff1f; app.json5 3、module.json5配置文件中&#xff0c;包含…

Java集合基础知识点复习

目录 Java提供的常见集合ListArrayList底层实现与扩容机制ArrayList listnew ArrayList(10)中的list扩容几次如何实现数组和List之间的转换用Arrays.asList转List后&#xff0c;如果修改了数组内容&#xff0c;list受影响吗&#xff1f;List用toArray转数组后&#xff0c;如果修…

uniapp项目问题及解决(前后端互联)

1.路由跳转的问题 uni.navigateTo&#xff08;&#xff09; 保留当前页面&#xff0c;跳转到应用内的某个页面&#xff0c;使用uni.navigateBack可以返回到原页面 uni.redirectTo&#xff08;&#xff09; 关闭当前页面&#xff0c;跳转到应用内的某个页面。 uni.reLaunch&…

揭开Spring Bean生命周期的神秘面纱

目录 一、Spring IOC 1.1 Spring IOC 的加载过程 二、Spring Bean 生命周期 2.1 实例化前置 2.2 实例化后置 2.3 属性赋值 2.4 初始化前置 2.5 初始化 2.6 初始化后置 2.7 Bean 销毁 Spring 是一个开源的企业级Java应用程序框架&#xff0c;它简化了企业级应用程序开…

开源数据湖iceberg, hudi ,delta lake, paimon对比分析

Iceberg, Hudi, Delta Lake和Paimon都是用于大数据湖(Data Lake)或数据仓库(Data Warehouse)中数据管理和处理的工具或框架,但它们在设计、功能和适用场景上有所不同。 Iceberg: Iceberg是用于大型分析表的高性能格式。Iceberg将SQL表的可靠性和简易性带入到大数据领域,同…

【java的本地锁到分布式锁介绍】

文章目录 1.java本地自带锁介绍及应用synchronized&#xff08;1&#xff09;synchronized原理和优化&#xff08;2&#xff09;synchronized作用&#xff08;3&#xff09;synchronized的使用 CAS(1) CAS原理&#xff08;2&#xff09;CAS和synchronized优缺点 lock 2.分布式锁…

idea常用配置

IDEA设置全局配置 参考&#xff1a;IDEA设置全局配置_idea如何打开一个项目,全局设置-CSDN博客 idea提交代码到git或svn上时&#xff0c;怎么忽略.class、.iml文件和文件夹等不必要的文件 参考&#xff1a;idea提交代码到git或svn上时&#xff0c;怎么忽略.class、.iml文件和文…

LeetCode-74. 搜索二维矩阵【数组 二分查找 矩阵】

LeetCode-74. 搜索二维矩阵【数组 二分查找 矩阵】 题目描述&#xff1a;解题思路一&#xff1a;先二分查找行&#xff0c;再二分查找列。解题思路二&#xff1a;暴力遍历&#xff0c;也能过。解题思路三&#xff1a;用python的in。 题目描述&#xff1a; 给你一个满足下述两条…

VSCODE目录树缩进调整

VSCode默认的缩进太小了&#xff0c;简直看不出来&#xff0c;很容易弄混目录。在设置里修改就行了。 修改后效果&#xff1a;

何为网络协议?一图知晓网络过程。

网络协议就是计算机之间沟通的语言 为了有效地交流&#xff0c;计算机之间需要一种共同的规则或协议&#xff0c; 就像我们和老外沟通之前&#xff0c;要先商量好用哪种语言&#xff0c; 要么大家都说中文&#xff0c;要么大家都说英语&#xff0c;这才能有效地沟通。 网络协…

OSPF中配置静态路由负载分担实验简述

OSPF中配置静态路由负载分担 实验简述 在静态路由负载分担中&#xff0c;多个路由器被配置为共享负载的目标&#xff0c;以实现流量的均衡分配。 到达目的地有N条相同度量值的路径&#xff0c;默认值60&#xff0c;N条路由是等价路由&#xff0c;数据报文在N条链路上轮流发送。…