个人主页 :阿然成长日记 点击可跳转
个人专栏: 数据结构与算法C语言进阶
不能则学,不知则问,耻于问人,决无长进
#include
using namespace std;
class Data
{
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()
{
//创建Data的对象1
Data d1;
d1.Init(1999, 1, 1);
//创建Data的对象2
Data d2;
d2.Init(2023, 1, 1);
//调用print函数
d1.print();
d2.print();
return 0;
}
上面代码运行结果中,为什么两个对象调用同一个函数能打印出不同的日期呢?或者说同一个函数如何做到结果不同呢?
简单说是通过C++的一个关键字this来实现的。
上面代码着编译后,就成了如下:
#include
using namespace std;
class Data
{
public:
void Init(int year,int month,int day)
{
_year = year;
_month = month;
_day = day;
}
void print(Data* this)
{
cout << this->_year << ":" << this->_month << ":" << this->_day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
//创建Data的对象1
Data d1;
d1.Init(1999, 1, 1);
//创建Data的对象2
Data d2;
d2.Init(2023, 1, 1);
//调用print函数
d1.print(&d1);
d2.print(&d2);
return 0;
}
当不同对象调用print函数时,其实在底层是传了一个Data*类型的指针this。
void print(Data* this)
{
cout << this->_year << ":" << this->_month << ":" << this->_day << endl;
}
> 如下图所示:
其实底层还是和C语言函数调用一样,也需要传参,只不过C++的编译器为我们默默做了这些事情,让我们用起来更方便。但是,不能显示的去写出this的相关实参和形参,需要注意的是在类中可以使用this->。
this本质是一个形参,哪个对象调用他,就指向哪个对象。
下图是代码在vs2019编译器下执行的汇编语句
。
当执行d1.Init(1999, 1, 1);时,先是将三个实参进行压栈。其实实参应该有4
个,还有一个隐藏的this指针
。
(1)000426EF push 1
(2)000426F1 push 1
(2)000426F3 push 7CFh
通过这一句(4)000426F8 lea ecx,[d1]
我们可以清晰的看到this指针在vs编译器下,是存到了寄存器( ecx)。
其实,this指针存在的位置一般是在栈帧上,不同的编译器有所不同,例如vs2019就是存在寄存器上。
因为this指针,占用内存小并且需要频繁使用,所以,将它放到寄存器里能大大提升运行效率。
下面代码会出现问题吗?
class A
{
public:
void Print()
{
cout << "Print()" << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();
return 0;
}
前提知识
p的意义有两个。1.p是A类一个对象的指针,p->print(),对象的指针调用函数肯定是找成员函数中的,也就是说编译的时候检查语法,让我们知道print函数是A类中的成员函数,起到定位语法作用。
p指针为空,相当于this指针为空。
成员变量存储在对象中
成员函数没有存储在对象中
p->Print();说明调用的是A类中的成员函数
调用print函数与this指针无关,只有访问成员变量时才会使用到this。
因为函数是在编译链接的时候找的(本质是找函数的地址),上一篇详细讲过对象的存储方式中讲过,成员函数存在公共区域的函数表(函数表中存有成员函数的地址)查找修饰后的函数名【因为C++支持重载,所以需要对函数名 进行特殊的修饰】来进行查找。
所以,代码运行正常。
看下面代码:
class A
{
public:
void PrintA()
{
cout<<_a<<endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->PrintA();
return 0;
}
printA函数中使用了成员变量,编译时printA函数底层如下形式:
void PrintA(Data* this)
{
cout<<this_a<<endl;
}
mian函数中是
p->PrintA(p);
将p传给了this指针。
_a=>this_a
,需要访问this指针,但此时this指针为空。
所以,运行崩溃!