关于this指针

关于this指针一个类的对象中实际只包含了该对象的数据成员信息,当我们创建了多个类的对象时,使对象1调用该类的成员函数,为什么可以改变对象1中的信息,而不去设置其他对象的信息?成员函数在类中只有一份,所有该类的对象共同使用,编译器是如何识别并处理的呢?编译器识别一个类分为三步:1.识别类的类名2.识别类的成员变量3.识别类的成员函数并对成员函数进行修改修改方式:成员函数有一个隐藏…

大家好,又见面了,我是你们的朋友全栈君。

一个类的对象中实际只包含了该对象的数据成员信息,当我们创建了多个类的对象时,使对象1调用该类的成员函数,为什么可以改变对象1中的信息,而不去设置其他对象的信息?成员函数在类中只有一份,所有该类的对象共同使用,编译器是如何识别并处理的呢?

编译器识别一个类分为三步:
1.识别类的类名
2.识别类的成员变量
3.识别类的成员函数并对成员函数进行修改

修改方式:
成员函数有一个隐藏的this指针,它指向调用该函数的对象
编译器为每个成员函数多加了一个参数,即this指针,它指向当前对象,并在函数内部的每个成员变量前都加上this指针

编译器是这样处理的:

class Person
{
public:
    void SetInfo(Person *this, const char * name, const char *gender, int age)
    {
        strcpy(this->_name, name);
        strcpy(this->_gender, gender);
        this->_age = age;
    }
private:
    char _name[20];
    char _gender[3];
    int _age;
};
this指针的性质:
  • this指针其本身的内容是不能被改变的,其类型为:类类型 * const
  • this指针不是对象本身的一部分,不影响该对象的大小
  • this指针的作用域在类的非静态成员函数的内部,只能在其内部进行使用,其他任何函数都不能,静态成员函数内部无this指针,后面会详述。
  • this指针是类中非静态成员函数的第一个默认隐含参数,编译器自动传递和维护,用户不可显示传递

函数调用约定
是指当一个函数被调用时,函数的参数会被传递给被调用函数,返回值会被返回给调用函数,总之,就是函数调用者与被调函数之间关于参数传递,返回值传递,堆栈清理,寄存器使用的一种约定。

它需要二进制级别兼容的强约定,函数调用者和函数体若使用不同的调用约定,可能会造成程序执行错误。

几种常用的调用约定:
这里写图片描述

其中,_cdecl是C/C++的默认调用约定,VC的调用约定中并没有_thiscall这个关键字,它是类成员函数默认调用约定;
C/C++中的main函数的调用约定必须是_cdecl,不允许更改。

对于_cdecl调用约定,为什么是调用者而不是函数体自己来平衡堆栈呢?

在这里我们应该要考虑类似于像scanf和printf这样的函数,这里我们应该明白这两个函数的参数都是可变的,如果参数不固定的话,在被调用函数内就无法知道参数究竟使用了多少个字节,所以为了实现可变参数,我们必须要在被调函数执行之后我们才知道参数究竟用了多少字节,所以我们在调用者来进行堆栈平衡操作。

这里我们重点说一下_thiscall调用约定:

  • 它只能用在类的成员函数上
  • 参数从右向左进行压栈
  • 若参数个数确定,this指针通过ecx寄存器传递给被调用者;若参数不确定,this指针在所有参数被压栈后压入堆栈。
  • 对于参数不确定的函数,调用者清理堆栈,否则函数自己清理堆栈。

问题:this指针是否可以为空?

上代码测一下先:

class Test
{
public:
    Test(int data = 0)
        :_data(data)
    {}
    void A()
    {
        cout << "A():" << this << endl;
    }
    void B()
    {
        cout << _data << endl;
    }
private:
    int _data;
};
int main()
{
    Test t;
    Test *p = NULL;
    p->A();
    p->B();
}

运行结果是:当走到 p->A();时,程序可以正常运行,运行结果是这:
这里写图片描述
而再接着往下走,走到p->B();时,程序崩溃。

为什么会这样呢?
分析一下:
函数A()中未调用任何成员变量
函数B()调用了成员变量_data
这里的p相当于一个this指针,当函数走到p->A();这一步时,编译器会将p交给ecx,再直接去调用A()函数,由于A()函数中未涉及取空指针中的内容,只是简单的打印,因此不会出现问题。
走到p->B();时,编译器会将_data修改成this->_data,而访问空指针中的内容程序一定崩溃。

因此,this指针是可以为空的,只要在成员函数内部不访问其内容,程序可以正常执行的,但是安全起见,我们还是不要让this指针为空指针最好。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/145721.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • centos7 本地yum源配置_centos官方yum源

    centos7 本地yum源配置_centos官方yum源准备上面三个文件。[root@localhost~]#yuminstall-ylibxml2-python-2.9.1-5.el7_0.1.x86_64.rpm[root@localhost~]#yuminstall-ycreaterepo-0.4.11-3.el5.noarch.rpm使用createrepo–version命令查看是否安装成功,出现…

  • springboot项目启动原理_转膜原理

    springboot项目启动原理_转膜原理1.总览上图为SpringBoot启动结构图,我们发现启动流程主要分为三个部分,第一部分进行SpringApplication的初始化模块,配置一些基本的环境变量、资源、构造器、监听器,第二部分实现了应用具体的启动方案,包括启动流程的监听模块、加载配置环境模块、及核心的创建上下文环境模块,第三部分是自动化配置模块,该模块作为springboot自动配置核心2.常用注解解释任何一个标注了@Bean的方法,其返回值将作为一个bean定义注册到Spring的IoC容器,方法名将默认成该bean定义的id.

  • python转置矩阵函数_对python 矩阵转置transpose的实例讲解

    python转置矩阵函数_对python 矩阵转置transpose的实例讲解在读图片时,会用到这么的一段代码:image_vector_len=np.prod(image_size)#总元素大小,3*55*47img=Image.open(path)arr_img=np.asarray(img,dtype=’float64′)arr_img=arr_img.transpose(2,0,1).reshape((image_vector_len,))#4…

  • 怎么搭建自己的web服务器_web服务器怎么搭建网站

    怎么搭建自己的web服务器_web服务器怎么搭建网站如今随着计算机和互联网技术的发展,上网现在已经不再是什么难事,打开浏览器,我们可以访问各种论坛站点,比如CSDN、博客园等,各种视频网站,例如爱奇艺,B站等。在网上我们可以写文章,看视频,购物,打游戏

  • debugview使用方法_debugger怎么用

    debugview使用方法_debugger怎么用1、什么是DebugView?DebugView是一个系统调试信息输出的捕获工具。2、DebugView它能干吗?可以捕获程序中由TRACE()和OutputDebugString()输出的信息。说的具体一点,在程序中使用如下函数:1)、OutputDebugString 或者在MFC中使用TRACE2)、内核模式中使用Out_Deb

    2022年10月28日
  • C#面试题及答案_c++面试题库

    C#面试题及答案_c++面试题库1.值变量/引用变量参考链接:https://www.cnblogs.com/bakuhert/articles/5878086.html2. 装箱/拆箱    装箱在值类型向引用类型转换时发生,拆箱在引用类型向值类型转换时发生,装箱操作和拆箱操作是要额外耗费cpu和内存资源的,所以在c#2.0之后引入了泛型来减少装箱操作和拆箱操作消耗。   参考链接:https://blo…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号