到底在哪存储在内存中的“这个”指针? 难道是在栈上分配的,在堆上,或在数据段?
#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
(通过将其作为一个隐含的第一个参数的函数)。
我认为这也非常有用,看看C ++ ISO规范明确地说这件事。 根据C ++ 03 ISO规格,§9.3.2/ 1:
在一个非静态(9.3)成员函数的主体,关键字this
是一个非左值表达式,其值是用于该函数被调用的对象的地址。
需要注意的是很重要的this
是不是一个变量-这是一个表达 ,多以同样的方式,表达1 + 2 * 3
是一个表达式。 这个表达式的值被允许几乎任何地方保存。 编译器可能把它的堆栈上,并将其传递作为隐式参数的功能,或者它可能把它放在一个寄存器中,并且它可以想到可以把它在堆或在数据段。 在C ++规范特意在这里给执行一定的灵活性。
我认为,“语言律师”的回答是:“这是完全实现定义,而且this
在技术上是一个指针,但计算结果为指针的表现。”
希望这可以帮助!
最简单的方法是想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,哪来的方法重载编组,是吧?” - 类型的异议,请。 :)
这个转变的问题为“存储在哪里有观点?”,答案当然是“看情况”。 :)
它往往是在栈中,但它可能是在寄存器中太,或编译器认为任何其他机制是良好的目标架构。
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
将被视为该方法的常规参数。
this
是一个右值(你不能把它的地址),所以它不(一定)占用内存的。 根据不同的编译器和目标体系结构,它往往会在寄存器中:在SPARC,ECX与MSVC英特尔,I0等当优化是积极的,它甚至可以左右移动。 (我用MSVC看到它在不同的寄存器)。
this
行为大多喜欢函数参数,因此将被存储在栈上或-如果该体系结构的二进制调用约定允许-在寄存器中。
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
指针。 它存储在机器寄存器,或者在功能激活的堆栈帧的存储器位置。