this指针和内存对齐:
-
当成员变量与成员函数形参名相同时,需要使用this指针,来区分是形参传递的值还是类对象成员。
class Student { public: char name[50]; int num; int age; public: /* void set_age(int age) { age = age; //Error }; //此处的成员函数会报错 */ //修改正确如下 void set_age(int age) { this->age = age; }; };
-
原因和道理:
-
我们应该这么做,但是内部原理是如何呢?首先来了解一下成员函数的存储方式:
当使用实例化定义一个对象时,系统便会为该对象分配存储的内存空间,举例:
//声明一个学生类(Student) class Student { public: char name[50]; int num; int age; public: void print_name() { cout << "name = " << name << endl; }; }; //用学生类(Student)定义一个对象(stud) Student stud; //为对象分配空间 int stud_size = sizeof(stud);
此处,
stud_size
应该是多少呢?50+4+4 = 58对吗,事实并非如此
可能的原因:a. 成员函数占了2字节
b.其他原因
既然如此,那我们就去掉成员函数,得到如下结果:
奇怪的是结果依旧如此,但是,据此我们可以发现:C++语言中每个对象所占用的存储空间只是该对象的数据成员所占用的存储空间,而不包括函数代码所占用的存储空间(因为针对某个类的多个对象而言,不同的只是数据成员,而函数成员来说都是一样的代码,所以没有必要为每个对象都保存一份成员函数的代码。这样可以很大程度上节省空间);继续回来到刚才的问题上,原因a被排除,引出**内存对齐**(便于CPU更方便的从内存中取数据):大小等于结构体中最大类型变量的整数倍【58÷4 = 14.5】✘【60÷4 = 15】✔将字节对齐设置为1,则:
#pragma pack(push) //保存对齐状态 #pragma pack(1) //设定为1字节对齐 class Student { public: char name[50]; int num; int age; public: void print_name() { cout << "name = " << name << endl; }; }; #pragma pack(pop) //恢复对齐状态
-
-
【this指针】在上述探究过程中发现:成员函数不占用存储空间。
-
既然成员函数不占用对象的存储空间,或者说多个对象是共用一个成员函数的,随之而来的问题便会出现:调用成员函数的时候,函数如何知道自己是被那个对象调用的呢?
Student zhang_san, li_si; strcpy(zhang_san.name, "zhang_san"); strcpy(li_si.name, "li_si"); zhang_san.print_name(); li_si.print_name(); //在这里函数是如何知道到底是对象zhang_san调用自己,还是li_shi呢? //答案:this指针
-
实现原理:
在每一个成员函数中都包含一个特殊的指针,这个指针的名字是固定的,称为 this,它也是C++中的保留关键字,它是指向本类对象的指针,它的值是当前被调用的成员函数所在的对象的起始地址。所以,当对象调用成员函数的时候,如:zhang_san.print_name(); 则系统会把 zhang_san 对象的地址赋值给 this 指针,所以在 print_name 函数的内部调用 cout 打印 name 成员的时候,实际上就是 this->name 这样调用的。只不过这里面的 this 可以省略不写,默认就是调用当前对象的。
-
形象的比喻:
当你进入一个房子后,你可以看见桌子、椅子、地板等,但是房子你是看不到全貌了。
对于一个类的实例来说,你可以看到它的成员函数、成员变量,但是实例本身呢?
this是一个指针,它时时刻刻指向你这个实例本身。
-
-
现在我们回到最开始,便会明白为什么要用this->age = age了
void set_age(int age) { this->age = age; //在此处this指向调用函数的具体对象如(zhang_san等),将形参传递过来的值(age)赋值给调用函数对象的(this->age) };