尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

this指针的认识+使用

this指针的认识+使用 关于C的this指针学习总结需要回答的问题this指针的认识this指针的本质一句话总结this的类型this的存储位置this指针的作用1.区分同名变量2.链式调用/连续赋值/连续调用(return *this)3.访问当前对象的地址或成员this指针的使用this指针与const的关系系统运行时会做以下的事情常方法const 与 static 的关系this可以为nullptr吗this 和智能指针有什么关系为什么不能在构造函数里调用虚函数并期望多态this和智能指针有什么关系const 与 friend需要回答的问题this指针是什么this存在哪里编译器怎么传递thisconst成员函数里this是什么类型this能为空吗为什么要用this指针静态函数为什么没有this指针…参考链接(整合了一下自己的理解和原文中更凝练的总结)链接: 卡码笔记this指针原理this指针的认识首先我们知道对象是属性和方法的集合但如果每次创建对象时都为其开辟属性方法的内存会消耗不必要的内存空间。eg 这里有相同类型的三个不同对象 a,b,c属性不同但方法相同若给每个对象都创建专属方法会消耗对应的内存但因为方法相同可以共用同一套方法进而节省空间如下图。这样做虽然节省了空间但是方法无法区分调动的是a,b,c哪个对象的属性所以为了其区分对象属性需要将对象的地址传入进而需要用到this指针。代码展示#include iostream using namepsace std; class Test { int num; public: Test(int x):num(x){} void Print() { cout this endl; } //打印this指针 }; int main() { Test a(1), b(2), c(3); cout a a endl; //打印a的地址 cout b b endl; //打印b的地址 cout c c endl; //打印c的地址 a.Print(); b.Print(); c.Print(); return 0; }运行结果 可以看到分为这样结果的三组 表明对象地址和this打印结果相同总结this指针指向调用该函数的对象this指针存在的根本意义是为了节约空间this指针的本质一句话总结this指针是编译器隐式传给非静态成员函数的第一个参数类型是指针常量Type* const。调用obj.func(x)时编译器实际生成的是fun(objx)把对象的地址作为第一个实参传入。this通常通过寄存器传递(x86用ecx,即thiscall约定)不占用栈空间。const成员函数的this类型就是const Type* const所以不能修改其成员。静态成员函数无this因为它不绑定任何对象实例。this的类型普通成员中this类型是T* const,也就是指针常量所谓指针常量就是本质是一个常量限制的是指针所以this的指向无法指向其他对象但指向的对象不是常量(可以修改成员)const成员函数中this类型变成const T* const指针本身不可变指向的对象也不可变。所以无法通过this修改任何非mutable成员。this的存储位置this是函数参数具体存在哪取决于调用约定。x86平台的thiscall约定中this通过ecx寄存器传递不占栈空间。x64和ARM平台也通常用寄存器传递前几个参数。只有在函数内部需要取this的地址时编译器才会把它溢出到栈上。平台寄存器x86ecxx64rdi / rcxARMr0this指针的作用1.区分同名变量示例class Test { int num; public: Test(int num) :num(num) { //构造函数 num 100; cout num num ,; cout this-num this-num endl; } void Print( ) { //无参打印函数 int num 100; num num; cout num num ,; cout this-num this-num endl; } void Print(int num) { //有参打印函数 num num; cout num num ,; cout this-num this-num endl; } }; int main() { Test t1(10); t1.Print(); t1.Print(100); return 0; }运行结果如下可以看到三个函数体内有关打印的代码一致先打印局部变量num再打印成员变量num第1行对应构造函数的结果 num100 this-num 10第2行对应有参打印函数的结果 num100 this-num 10第3行对应无参打印函数的结果 num100 this-num 10则在函数体中 num100numnum 其实都是对局部变量的改变即num100是对局部变量的值进行修改numnum是局部变量给自身赋值由此可知在构造函数中初始化列表的num系统可以区分同名的成员变量和局部变量并正确赋值也就是说num(num) 本质上等价于 this-num num。在打印函数和构造函数体中不论是形参名还是变量名只要与成员变量名相同必须用this指针来和成员变量区分否则系统会优先选择局部变量而不是成员变量。若函数体中不存在和成员变量名相同的局部变量则系统可以正常识别成员变量2.链式调用/连续赋值/连续调用(return *this)解引用 this 得到当前对象本身类型是 T返回它就是返回对象的引用。链式调用 obj.set(1).set(2).set(3) 就是靠每个方法 return *this 实现的——每次返回的都是同一个对象的引用所以可以继续调用它的方法。在重载赋值函数中常见代码如下class Test { int num; public: Test(int num) :num(num) { } Test operator(const Test t) { //重载赋值函数 if (this ! t) { num t.num; } return *this; } void Print() { cout num endl; } }; int main() { Test t1(10); Test t2 t1; t1.Print(); t2.Print(); return 0; }运行结果如下这里的Test t2 t2 相当于得到 t2.operaort1的返回值这里返回类型为Test搭配*this 可以更高效的返回对象本身3.访问当前对象的地址或成员这里比较好理解用最简单的方法呈现一下class Test { int num; public: Test(int num) :num(num) { cout this this; cout this-num this-num endl; } }; int main() { Test t1(10); return 0; }结果如下this指针的使用this指针与const的关系除了知道this指针上述三个作用外还需要知道系统是如何去使用this指针进而理解this指针与const的关系。首先要始终牢记this指针指向对象再去解释后面的内容。系统运行时会做以下的事情识别类内的属性包括value值 int类型 private访问类型识别类内的方法的声明暂时不看方法体改写 即编译时添加形参 返回值 添加this指针修改调用 传入this指针示例如下class Test{ int value; public: void SetValue(int x) { value x; } //改写为 //void SetValue(Test* const this,int x){this-value x;} int GetValue() { return value; } //改写为 //int GetValue(Test* const this){return this-value;} }; int main() { Test t1; t1.SetValue(200); //修改为 t1.SetValue(t1,200); cout t1.GetValue() endl; //修改为 t1.GetValue(t1); return 0; }系统去改写就是为了传入当前对象的地址值得注意的时这里系统还加入了const如void SetValue(Test* const this,int x)是为了防止在函数体中对this本身进行修改如thisnullptr等改变this指向的操作确保了代码的正确与安全。常方法系统传入this指针时会自己加入const 如Test* const this有时我们还会自己定义常方法在函数名末尾加const示例class Test{ int value; public: void SetValue(int x) { value x; } int GetValue() const { return value; } //改写为 //int GetValue(const Test* const this){return this-value;} }; int main() { Test t1; t1.SetValue(200); cout t1.GetValue() endl; return 0; }这里让 GetValue 变为常方法 int GetValue() const加入该 const 后系统在改写时还会对this指针再加一个const修饰改写为int SetValue(const Test* const this,int x)让this指针变成只读指针无法修改this的指向也无法修改this本身从而实现常方法。注意常对象只能调用常方法 普通函数都可以调用int fun(); int fun()const; const可以作为函数重载的依据因为有const所以形参列表不同同名同返回值的普通函数和常方法 普通对象优先调用普通方法 常对象调用常方法对于不修改对象属性值的方法让其定义或重载为常方法可以提高代码灵活性const 与 static 的关系1.常方法与静态变量之间的关系示例class Test { int value; static int num; public: void fun() const { cout num endl; } }; int Test::num 0; int main() { Test t1; t1.fun(); return 0; }运行结果为1静态变量不为某个对象所有是为整个类型服务的总共只有一份静态变量也就不受制与 this 指针而常方法修饰的是this指针所以代码正常运行num结果1。2.静态方法与const的关系静态方法可以直接调用不需要用this指针来区分静态成员函数属于类而不属于任何对象实例调用时不需要对象T::staticFunc()编译器不会传入对象地址所以没有 this。这也是静态函数不能访问非静态成员的根本原因——没有对象地址无法定位成员的内存位置。class Test { int value; static int num; public: // static void fun() const { //error static void fun() { cout num endl; //cout value endl; //error } }; int Test::num 0; int main() { Test t1; t1.fun(); return 0; }注意静态方法不需要this也可以调用系统不会添加this指针 那么也无法在函数名末添加const静态方法无this指针无法区分对象也无法访问对象成员this可以为nullptr吗语法上可以构造出来((T*)nullptr)-func()但这是未定义行为。如果 func 内部不访问任何成员变量某些编译器下可能不崩溃但这纯属巧合不能依赖。任何通过空 this 访问成员的操作都会段错误。this 和智能指针有什么关系this 是裸指针不管理生命周期。如果需要在对象内部获取指向自身的 shared_ptr不能直接 shared_ptr(this)会导致双重释放必须继承 std::enable_shared_from_this然后调用 shared_from_this()。为什么不能在构造函数里调用虚函数并期望多态构造函数执行时对象的动态类型还是当前正在构造的类vptr 指向当前类的 vtablethis 的静态类型也是当前类。所以即使调用虚函数也只会调到当前类的版本不会调到派生类的重写版本。this和智能指针有什么关系this是裸指针不管理生命周期。如果需要在对象内部获取指向自身的shared_ptr,不能直接shared_ptr(this) 会导致双重释放必须寂静城std::enable_shared_from_this,然后调用shared_from_this()。const 与 friend友元函数 属于外部函数class Test { int value; public: Test(int x):value(x){} friend void fun(const Test b); }; void fun(const Test b) {cout b.value endl;} int main() { Test t1(10); fun(t1); return 0; }友元函数无this指针也不自动添加无法用const修饰
返回列表