2013-03-03 5 views
2

У меня есть сомнения: я могу объявить указатель на функцию-член классаC++ представление объекта

void (*MyClass::myFunc)(void); 

и я могу объявить указатель на переменную-член класса

int (MyClass::*var); 

Мой вопрос: как объект (состоящий из функций-членов и переменных-членов), структурированных в памяти (asm-level)?

Я не уверен, потому что, за исключением полиморфизма и виртуальных функций времени выполнения, я могу объявить указатель на функцию-член даже без объекта, и это означает, что функции кода разделяются между несколькими классами (хотя для них требуется * этот указатель работает нормально)

Но как насчет переменных? Почему я могу объявить указатель на переменную-член даже без экземпляра объекта? Конечно, мне нужно его использовать, но тот факт, что я могу объявить указатель без объекта, заставляет меня думать, что объект класса в памяти представляет его переменные с указателями на другие области памяти.

Я не уверен, если я объяснил правильно мои сомнения, если не просто дайте мне знать, и я постараюсь объяснить это лучше

ответ

3

Классы хранятся в памяти достаточно просто - почти так же, как структуры. Если вы проверите память в том месте, где хранится экземпляр класса, вы заметите, что его поля просто упакованы один за другим.

Есть разница, если у вашего класса есть виртуальные методы. В этом случае первое, что хранится в экземпляре класса, является указателем на таблицу виртуальных методов, что позволяет виртуальным методам работать правильно. Вы можете узнать больше об этом в Интернете, это немного более сложная тема. К счастью, вам не о чем беспокоиться, компилятор делает все для вас (я имею в виду, что обрабатываю 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.

+0

Хороший ответ, но как насчет указателя на переменные-члены? Связаны ли переменные между экземплярами? –

+0

Я изменил свой ответ. Так понятно? – Spook

+0

Да, большое вам спасибо! –

Смежные вопросы