Где указатель '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'. Где this хранится в памяти?

Ответ 1

Другие ответы проделали очень хорошую работу, объясняя, как типичный компилятор реализует this (передавая его как неявный первый параметр функции).

Я думаю, что также полезно посмотреть, что конкретно говорит об этом в спецификации С++ ISO. Согласно спецификации С++ 03 ISO, & sect; 9.3.2/1:

В теле нестатической (9.3) функции-члена ключевое слово this является выражением не-lvalue, значением которого является адрес объекта, для которого вызывается функция.

Важно отметить, что this не переменная - это выражение, почти так же, как выражение 1 + 2 * 3 является выражением. Значение этого выражения разрешено хранить практически везде. Компилятор может поместить его в стек и передать его как неявный параметр функции, или он может поместить его в регистр, и он, предположительно, может поместить его в кучу или в сегмент данных. Спецификация С++ преднамеренно дает реализацию некоторую гибкость здесь.

Я думаю, что ответ "язык-юрист" - "это полностью реализация, и, кроме того, this технически не является указателем, а выражением, которое оценивает указатель".

Надеюсь, это поможет!

Ответ 2

Самый простой способ - думать о 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, где сортировка для перегрузки метода, да?" - типа возражений, пожалуйста.:)

Это превращает вопрос в "где хранятся аргументы?", и ответ, конечно, "это зависит".:)

Это часто в стеке, но может быть и в регистрах, или любой другой механизм, который компилятор считает полезным для целевой архитектуры.

Ответ 3

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 рассматривается как регулярный параметр этого метода.

Ответ 4

this является rvalue (вы не можете взять его адрес), поэтому он не (обязательно) занимают память вообще. В зависимости от компилятора и целевой архитектуры, он часто будет в регистре: i0 на Sparc, ECX с MSVC на Intel и т.д. Когда оптимизатор активный, он может даже передвигаться. (Я видел это в разных регистры с MSVC).

Ответ 5

this ведет себя в основном как аргумент функции и, как таковой, будет храниться в стеке или - если двоичные соглашения о вызовах архитектуры позволяют это - в регистре.

Ответ 6

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. Он хранится в машинном регистре или в ячейке памяти в кадре стека активации функции.