引言

前一节类的编译流程中简单介绍了this指针的类型,但没说为什么是这种定义方式。this指针的原型就是类名* const this,其实也就是为什么*的右边需要加个const,原因很简单:害怕程序员修改this指针的值,比如说:置空this = NULL; 这种操作对代码的后续执行是致命的。


(一)this指针+const

看了引言的小伙伴就很疑惑,明明都已经说过了this加const的情况,那为何还有const可加??

这是之前讲过的栗子:

1
2
3
int a = 10;
int* const p = &a;
const int* const cp = &a;

可以看到上面的p就相当于对象中的那个this指针的类型(依赖于对象),而cp就是我所谓的this指针+const了。很明显,并不重复。

C++中也为this指针提供了这种定义方式:(常方法)

返回值 方法名(形参列表)const;

()后面的const修饰的就是对应方法的第一个参数this指针,形式如下:const 类名* const this,这里的左const就是我们在()后面加的const,右const是编译器在编译时期加的。


(二)常对象

有一句话叫做:常对象只能调用常方法。上面已经讲述了常方法,那么这句话的意思也大概能够参透了。

这句话意味着:

  • 常对象只能调用常方法;
  • 普通对象可以调用所有方法;

下面我举个栗子解释下为什么是这样:

你能看出这段代码有什么问题吗??

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
using namespace std;
//设计个人类
class Person
{
public:
Person(); //无参构造
...
int GetAge()const; //常方法
char* GetName(); //普通方法
private:
int age;
char* name;
};
int main()
{
//普通对象
Person p1; //假设已经初始化
p1.GetAge(); //GetAge(&p1) 正确
p1.GetName(); //GetName(&p1) 正确

//常对象
const Person p2; //假设已经初始化
p2.GetAge(); //GetAge(&p2)正确
p2.GetName(); //GetName(&p2) 错误
return 0;
}

分析:

  • 先对p1分析:p1是一个Person类型的普通对象调用常方法GetAge(),编译器在编译阶段将该语句编译成GetAge(&p1);,使用const Person const 类型的this指针指向p1的地址(Person类型),能力缩小,这是完全可行的。那么GetName()也自然可行,编译成功
  • p2分析:p2的地址类型(const Person),在调用常方法GetAge(),编译期间被改写成GetAge(&p2),此时使用const Person const this指针指向p2,未对p2的产生影响,可行,编译成功。但是在调用GetName()时,此时的this指针是Person* const类型,而p2的地址(const Person),this保存p2的地址扩大了p2的能力,编译不通过*所以常对象只能调用常方法

(三)this指针的传递方式

this指针的调用跟调用约定相关。

(1)thiscall调用约定

thiscall仅仅应用于C++成员方法。

1
2
lea ecx,[p1]	;将p1地址给ecx寄存器
mov [this], ecx ;将ecx中的值给this指针

在这里插入图片描述

(2)__cdecl调用约定

在这里插入图片描述

结论:__cdecl 比 thiscall 多了一次push和pop