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