66

'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内存中存储在哪里?

4

6 回答 6

79

最简单的方法是将其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);

请注意,上面的转换是简化的,希望能让我的观点更清楚一点。无需在评论中填写“哇,方法重载的编组在哪里,嗯?”-类型的反对,请。:)

这将问题转化为“参数存储在哪里?”,答案当然是“取决于”。:)

它通常在堆栈上,但也可能在寄存器中,或者编译器认为对目标架构有益的任何其他机制。

于 2013-05-16T10:56:56.827 回答
65

其他答案很好地解释了典型编译器的实现this方式(通过将其作为隐式第一个参数传递给函数)。

我认为了解 C++ ISO 规范对此的明确说明也很有用。根据 C++03 ISO 规范,§9.3.2/1:

在非静态 (9.3) 成员函数的主体中,关键字this是一个非左值表达式,其值是调用该函数的对象的地址。

重要的是要注意这不是this一个变量——它是一个表达式,就像表达式是一个表达式一样。这个表达式的值几乎可以存储在任何地方。编译器可能将它放在堆栈上并将其作为隐式参数传递给函数,或者它可能将它放在寄存器中,并且可以想象它可以将它放在堆或数据段中。C++ 规范特意为实现提供了一些灵活性。1 + 2 * 3

我认为“语言律师”的答案是“这是完全由实现定义的,而且this在技术上不是指针,而是计算为指针的表达式。”

希望这可以帮助!

于 2013-05-16T15:54:43.997 回答
37

this通常作为方法的隐藏参数传递(不同调用约定的唯一区别是how)。

如果你打电话:

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被视为此方法的常规参数。

于 2013-05-16T10:59:57.880 回答
18

this是一个右值(你不能获取它的地址),所以它根本不会(必然)占用内存。根据编译器和目标架构,它通常位于寄存器中:Sparc 上的 i0、Intel 上的 ECX 和 MSVC 等。当优化器处于活动状态时,它甚至可以四处移动。(我已经在 MSVC 的不同寄存器中看到它)。

于 2013-05-16T11:28:00.743 回答
11

this其行为主要类似于函数参数,因此将存储在堆栈中,或者 - 如果体系结构的二进制调用约定允许 - 在寄存器中。

于 2013-05-16T10:57:12.580 回答
1

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
}

我一个函数激活链,也会有this指针的多个副本。假设我们有一个对象obj并调用它的foo函数。该函数调用同一个对象的bar函数,并bar调用另一个名为update. 每个功能激活级别都有this指针。它存储在机器寄存器中,或者存储在函数激活的堆栈帧中的内存位置中。

于 2013-05-16T15:08:28.583 回答