从汇编角度,清晰的去看构造函数和this指针到底是个什么东西呢?也许可以解决你的一点小疑问
首先写一个很简单的代码demo:
class A{
public:int a;A(){;}void seta(int _a){a=_a;}A* getA(){return this;}
};int fun1(int px){return px;
}int main(){A aa;aa.seta(8); aa.getA();
}
mov rbp, rsp
:设置新的基址指针,指向当前栈帧。
mov QWORD PTR [rbp-8], rdi
:将 this
指针(存储在 rdi
中)保存到栈上。
在seta中的部分汇编:
mov rax, QWORD PTR [rbp-8]
:将 this
指针的值加载到 rax
中。
mov DWORD PTR [rax], edx
:将 edx
(即 _a
的值)存储到 this->a
中。
我们从类成员函数和构造函数中都能看到 两句关于参数的汇编,同理普通函数中px参数也有着相同的汇编,那么很显然,对于cpu而言,this指针仅仅是一个参数而已。C++语法糖对于this做了隐藏处理,因此我们在使用的时候才会无感。
而这个有这个this往往可以认为函数的完整使用是这样:
A aa;aa.seta(8); ====A::seta(&aa,8) 当然这么调用是错误的,看着很像类静态函数
this
指针的创建
- 函数调用前创建:当成员函数被调用时,
this
指针在调用成员函数之前被设置为当前对象的地址。也就是说,在成员函数执行之前,this
指针已经被创建并指向调用该函数的对象。
this
指针的销毁
- 函数调用结束后销毁:在成员函数执行完毕并返回后,
this
指针的生命周期就结束了。因为this
只是一个指向对象的指针,当函数返回时,不再需要这个指针,函数调用上下文会自动清理这个隐式参数。
学习C++的友友大概率会知道这么一句话:静态成员函数不属于类的某个具体对象,而是属于整个类。这意味着静态成员函数不能访问非静态成员变量和成员函数,因为它们没有 this
指针。针对这句话我们也可以通过cpu视角进行观察
而this
指针的存在条件
- 对象实例调用:只有当对象实例调用非静态成员函数时,
this
指针才会被传递给该函数。 - 隐式参数:在非静态成员函数内部,编译器会自动添加一个隐式的
this
指针参数,用于引用调用该函数的对象
观察上图,你就会看到 静态成员函数中没有this指针,即也就没法去调用相关的成员函数和数据,如果我们想要通过静态成员函数去调用对象相关的数据,可以通过黄色框框的这种写法。你会发现这种写法的汇编同成员函数的汇编相同,也就是说this存的就是传递进去的这个对象的地址。
也就有了成员函数的完整版应该是:void setp(A* this,int p);是不是看着参数和示例第二个静态成员函数一样~
cpu眼中构造函数与普通函数没有任何区别:
通过上面的汇编也能看明白,构造含有this指针,且对于cpu来说他与普通函数是相同的
派生类构造函数总会调用基类的构造函数
mov QWORD PTR [rbp-8], rdi
:将this
指针(在rdi
中)保存到局部变量中。mov rax, QWORD PTR [rbp-8]
:将this
指针从局部变量中加载到rax
中。mov rdi, rax
:将this
指针(现在在rax
中)移动到rdi
中,以便调用基类构造函数时使用。call A::A() [base object constructor]
:调用基类A
的构造函数。此时,this
指针指向B
对象,但调用的是A
的构造函数,因此它会正确初始化A
的部分。mov rax, QWORD PTR [rbp-8]
:再次将this
指针从局部变量中加载到rax
中。mov DWORD PTR [rax+4], 1
:将1
赋值给this->a
。这里的[rax+4]
指的是b
位于对象内偏移量 4 的位置。 (因为B继承A,B所以内存结构上,前四个字节是A.a ,然后才是B.a ,所以是移动到偏移4位置之后在赋值)