'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'指针。 this
存储在内存中的哪个位置?
答案 0 :(得分:78)
最简单的方法是将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填写评论,哪里是方法重载的编组,是吗?” - 请输入异议。 :)
将问题转换为“存储参数的位置?”,答案当然是“它取决于”。 :)
它通常在堆栈上,但它也可能在寄存器中,或者编译器认为适用于目标体系结构的任何其他机制。
答案 1 :(得分:63)
其他答案在解释典型编译器如何实现this
(通过将其作为隐式第一个参数传递给函数)方面做得非常好。
我认为看看C ++ ISO规范明确说明了什么也很有用。根据C ++ 03 ISO规范,§9.3.2/ 1:
在非静态(9.3)成员函数的主体中,关键字
this
是一个非左值表达式,其值是调用该函数的对象的地址。
重要的是要注意this
不变量 - 它是表达式,与表达式1 + 2 * 3
的方式非常相似一种表达。允许将此表达式的值存储在任何地方。编译器可能将它放在堆栈上并将其作为隐式参数传递给函数,或者可能将其放入寄存器中,并且可以想象它可以将其放入堆或数据段。 C ++规范在这里故意为实现提供了一些灵活性。
我认为“语言 - 律师”的答案是“这是完全实现定义的,而且this
在技术上不是指针,而是一个计算指针的表达式。”
希望这有帮助!
答案 2 :(得分:33)
this
通常作为方法的隐藏参数传递(不同调用约定的唯一区别是如何)。
如果你打电话:
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
我强制编译器通过堆栈传递所有参数。如果您随后启动调试器并检查汇编代码,您将找到如下内容:
myClass.Method(5);
00AA31BE push 5
00AA31C0 lea eax,[myClass]
00AA31C3 push eax
00AA31C4 call MyClass::Method (0AA1447h)
如您所见,方法的参数通过堆栈传递,然后myClass的地址被加载到eax寄存器并再次被压入堆栈。换句话说,this
被视为此方法的常规参数。
答案 3 :(得分:18)
this
是一个右值(你不能拿它的地址),所以它没有
(必然)占据记忆。取决于编译器
和目标架构,它通常在寄存器中:i0
在Sparc上,ECX与Intel上的MSVC等等。优化器是
活跃,它甚至可以四处走动。 (我在不同的地方看过它
向MSVC注册)。
答案 4 :(得分:9)
this
的行为大多类似于函数参数,因此将存储在堆栈中,或者 - 如果架构的二进制调用约定允许 - 在寄存器中。
答案 5 :(得分:0)
this
未存储在明确定义的位置!它指向的对象存储在某处,并且具有明确定义的地址,但地址本身没有特定的家庭地址。它在计划中传播。不仅如此,还可以有许多指针的副本。
在下面的虚构init
函数中,对象注册自身以接收事件和计时器回调(使用虚构的事件源对象)。因此,在注册后,还有两个this
的副本:
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
的函数。每个函数激活级别都有this
指针。它存储在机器寄存器中,或存储在功能激活的堆栈帧的存储单元中。