Классы хранятся в памяти достаточно просто - почти так же, как структуры. Если вы проверите память в том месте, где хранится экземпляр класса, вы заметите, что его поля просто упакованы один за другим.
Есть разница, если у вашего класса есть виртуальные методы. В этом случае первое, что хранится в экземпляре класса, является указателем на таблицу виртуальных методов, что позволяет виртуальным методам работать правильно. Вы можете узнать больше об этом в Интернете, это немного более сложная тема. К счастью, вам не о чем беспокоиться, компилятор делает все для вас (я имею в виду, что обрабатываю VMT, а не беспокоиться).
Перейдем к методам. Когда вы видите:
void MyClass::myFunc(int i, int j) { }
На самом деле компилятор преобразует его в нечто вроде:
void myFunc(MyClass * this, int i, int j) { }
И когда вы звоните:
myClassInstance->myFunc(1, 2);
Компилятор генерирует следующий код:
myFunc(myClassInstance, 1, 2);
Ple ase имейте в виду, что это упрощение - иногда это немного сложнее, чем это (особенно, когда мы обсуждаем вызовы виртуальных методов), но он показывает более или менее то, как классы обрабатываются компилятором. Если вы используете какой-то низкоуровневый отладчик, такой как WinDbg, вы можете проверить параметры вызова метода, и вы увидите, что первый параметр обычно является указателем на экземпляр класса, на который вы вызвали метод.
Теперь все классы одного типа совместно используют двоичные файлы своих методов (скомпилированный код). Поэтому нет смысла копировать их для каждого экземпляра класса, поэтому в памяти хранится только одна копия, и все экземпляры ее используют. Теперь должно быть ясно, почему вы можете получить указатель на метод, даже если у вас нет экземпляра класса.
Однако, если вы хотите вызвать метод, хранящийся в переменной, вам всегда нужно предоставить экземпляр класса, который может быть передан скрытым «этим» параметром.
Edit: В ответ на комментарии
Вы можете прочитать больше о членах указателей в another SO question. Я предполагаю, что указатель на член хранит разницу между началом экземпляра классов и указанным полем. Когда вы пытаетесь получить значение поля с использованием указателя на член, компилятор находит начало экземпляра классов и перемещается на количество байтов, хранящихся в указателе на элемент, для достижения указанного поля.
Каждый экземпляр класса имеет свою собственную копию нестатических полей - в противном случае они были бы не очень полезны для нас.
Обратите внимание, что аналогично указателям на методы вы не можете использовать указатель на элемент напрямую, вам снова нужно предоставить экземпляр класса.
Доказательство того, что я говорю, все будет в порядке, так вот она:
class C
{
public:
int a;
int b;
};
// Disassembly of fragment of code:
int C::*pointerToA = &C::a;
00DB438C mov dword ptr [pointerToA],0
int C::*pointerToB = &C::b;
00DB4393 mov dword ptr [pointerToB],4
Вы можете увидеть значения, хранящиеся в pointerToA и pointerToB? Поле a
датируется 0 байтами от начала экземпляра классов, поэтому значение 0 сохраняется в указателеToA. С другой стороны, поле b
сохраняется после поля a
, длина которого составляет 4 байта, поэтому значение 4 хранится в указателеToB.
Хороший ответ, но как насчет указателя на переменные-члены? Связаны ли переменные между экземплярами? –
Я изменил свой ответ. Так понятно? – Spook
Да, большое вам спасибо! –