目录
- this指针
- this指针的引出
- this指针的特性
- this指针相关例题
- 例题1
- 例题2
感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接
🐒🐒🐒 个人主页
🥸🥸🥸 C语言
🐿️🐿️🐿️ C语言例题
🐣🐣🐣 python
🐓🐓🐓 数据结构C语言
🐔🐔🐔 C++
🐿️🐿️🐿️ 文章链接目录
this指针
this指针的引出
我们先来看看下面的代码
class Date
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "/" << _month << "/" << _day << endl;}int _year;int _month;int _day;
};
int main()
{Date d1;d1.Init(2024, 5, 4);Date d2;d2.Init(2024, 5, 5);d1.Print();d2.Print();return 0;
}
结果如下
对应 d1.Print()和 d2.Print()在上一篇文章中就已经讲过
他们都是访问的同一个函数,因为print是在一块公共区域里
并且print中的_year _month _day并不是类声明的成员变量,因为上一篇文章提到过,成员变量_year _month _day只是声明,并没有占用空间,因此如果把这些没有占用空间的成员变量打印出来是做不到的
所以print函数打印的是定义出来的成员变量,也就是初始化后的成员变量
而Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用 Init 函数时,函数是如何知道应该设置d1对象,而不是设置d2对象呢
C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
具体过程如下
当d1想要调用print函数时就将d1的地址传给函数,然后print函数通过d1的地址去找他的成员变量,就可以不会搞错了
如果没有this指针的话print函数就可能找错对象,就可能会出现d1调用print的时候,print找的是d2的成员变量
this指针的特性
- this指针的类型:类类型* const (*const this),即成员函数中,不能给this指针赋值。
- 只能在“成员函数”的内部使用
- this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
- this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递
- this指针在形参和实参的位置不可以显示写,但是可以在函数内部使用
补充:this指针是存在栈上的,因为this指针是一个形参,因为形参是实参的拷贝,当调用函数的时候,会建立栈帧,形参会拷贝实参,然后存在栈帧中,函数调用结束后空间就会被销毁,有些编译器比较特殊(vs可能会用寄存器存储,因为this指针使用比较频繁,并且由于寄存器访问速度比较快,所以就将this指针放在寄存器里)
this指针相关例题
例题1
下面程序编译运行结果是
A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:void PrintA(){cout << _a << endl;}
private:int _a;
};
int main()
{A* p = nullptr;p->PrintA();return 0;
}
答案:B
这道题和上一篇文章扩展中的例题非常像,他们的区别就是一个访问的是类的成员还有类的函数,另一个只访问的类的函数
class Date
{
public:void Init(){cout << "Date Init" << endl;}
};
int main()
{Date test;Date* p2 = nullptr;p2->Init();return 0;
}
只访问函数的可以正常运行,因为存储的空间不在对象里
访问成员变量的会运行崩溃,因为类的成员并不像类的函数一样,会单独存放在一块公共空间里,当类的指针为空时,里面的成员也就不存在
并且也可以用this指针来解释,Init函数调用的时候会将p2做完this指针指向的对象传进去,由于函数里需要去访问成员变量,所以会根据对象的地址去寻找成员变量,但是对象根本不存在,所以就会崩溃
下图也说明了传入的this指针为空
例题2
下面程序编译运行结果是
A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:void PrintA(){cout << this << endl;cout << "Print()" << endl;}
private:int _a;
};
int main()
{A* p = nullptr;(*p).PrintA();return 0;
}
答案:C
这里的(*p).PrintA()和之前的p->PrintA()是一样的,因为只是访问类的函数,所以不管p是不是空都可以正常运行
从汇编中也可以看到,(*p).PrintA()和p->PrintA()调用的函数地址都是存在的,就是因为类的函数会存在一块公共区域,在编译的时候就已经确定了
并且(*p).PrintA()和p->PrintA()并不会对p解引用,因为没有访问类的成员,所以不需要解引用,只需要给PrintA()传入this指针就可以了