计算机存储器中存储的“this”指针在哪里?
“this”指针在哪里存储在内存中? 它是分配在堆栈中,堆中还是数据段中?
#include <iostream> using namespace std; class ClassA { int a, b; public: void add() { a = 10; b = 20; cout << a << b << endl; } }; int main() { ClassA obj; obj.add(); return 0; }
在上面的代码中,我调用成员函数add()
,接收者对象作为“this”指针隐式传递。 这里存储在内存中?
其他答案已经做了一个很好的工作,解释了一个典型的编译器如何实现(通过将其作为一个隐含的第一个parameter passing给函数)。
我认为看看C ++ ISO规范明确地说明了这一点也是有用的。 根据C ++ 03 ISO规范,§9.3.2/ 1:
在非静态(9.3)成员函数的主体中,关键字
this
是一个非左值expression式,其值是调用该函数的对象的地址。
重要的是要注意, this
不是一个variables – 这是一个expression式 ,就像expression式1 + 2 * 3
。 这个expression式的值允许存储在任何地方。 编译器可能把它放在堆栈上,并把它作为一个隐式parameter passing给一个函数,或者把它放到一个寄存器中,并且可以把它放在堆或数据段中。 C ++规范在这里故意给予实现一些灵活性。
我认为“语言律师”的答案是“这是完全实现定义的,而且this
在技术上不是一个指针,而是一个评估指针的expression式”。
希望这可以帮助!
最简单的方法就是将this
视为一个总是自动传递的隐藏的额外参数。
所以,一个虚构的方法,如:
size_t String::length(void) const { return strlen(m_string); }
实际上更像是这样下的:
size_t String__length(const String *this) { return strlen(this->m_string); }
和一个像这样的电话:
{ String example("hello"); cout << example.length(); }
变成这样的东西:
cout << String__length(&example);
请注意,上面的转换已经简化了,希望能够让我的观点更清晰。 没有必要用“whaaa,方法重载编组在哪里?”填写注释,请input异议。 🙂
这个问题转化为“存储参数在哪里?”,答案当然是“取决于”。 🙂
它通常在堆栈中,但也可能在寄存器中,或者编译器认为对目标架构有利的任何其他机制。
this
通常是作为方法的隐藏parameter passing的(在不同的调用约定中,唯一的区别是如何 )。
如果你打电话给:
myClass.Method(1, 2, 3);
编译器生成以下代码:
Method(&myClass, 1, 2, 3);
第一个参数实际上是指向this
的指针。
我们来看看下面的代码:
class MyClass { private: int a; public: void __stdcall Method(int i) { a = i; } }; int main(int argc, char *argv[]) { MyClass myClass; myClass.Method(5); return 0; }
通过使用__stdcall
我强制编译器通过堆栈传递所有参数。 如果您然后启动debugging器并检查汇编代码,则会发现如下内容:
myClass.Method(5); 00AA31BE push 5 00AA31C0 lea eax,[myClass] 00AA31C3 push eax 00AA31C4 call MyClass::Method (0AA1447h)
正如你所看到的,方法的参数通过堆栈传递,然后myClass的地址被加载到eax寄存器并再次被压入堆栈。 换句话说, this
被视为这种方法的常规参数。
this
是一个右值(你不能接受它的地址),所以它不(必然)占用内存。 根据编译器和目标体系结构的不同,它通常会在一个寄存器中:Sparc中的i0,Intel上的MSVC上的ECX等。当优化器处于活动状态时,甚至可以四处移动。 (我已经看到它与MSVC不同的寄存器)。
this
行为大部分就像一个函数参数,因此会被存储在堆栈中,或者如果体系结构的二进制调用约定允许在寄存器中。
this
不是存储在一个明确的位置! 它指向的对象存储在某个地方,并有一个定义明确的地址,但地址本身并没有一个特定的家庭地址。 它在程序中传达。 不仅如此,该指针还可以有很多副本。
在下面的想象的init
函数中,对象注册自己接收事件和定时器callback(使用假想的事件源对象)。 所以在注册之后,还有两个这样的副本:
void foo_listener::init() { g_usb_events.register(this); // register to receive USB events g_timer.register(this, 5); // register for a 5 second timer }
我的一个函数激活链,也会有这个指针的多个副本。 假设我们有一个对象obj
并调用它的foo
函数。 该函数调用同一个对象的bar
函数, bar
调用另一个叫update
函数。 每个function激活级别都有this
指针。 它存储在机器寄存器中,或存储在函数激活的堆栈帧的存储单元中。