В процессе написания очередного файлового менеджера, задался вопросом: «Каким же образом происходит деление на частные, защищенные и публичные объекты в классах?». Как оказалось, никакого деления нет — это чистая абстракция.
Ниже описано строение и функционирование классов на языке C++, а так же способы обходов запретов частных полей доступа.
Классы
И так, что же такое класс. Классы – это некая абстракция, представляющая собой совокупность атрибутов и методов класса или, как говорит нам Интернет:
Класс — разновидность абстрактного типа данных в объектно-ориентированном программировании, характеризуемый способом своего построения.
Класс, на самом деле, – это простая структура, не хранящая в себе никаких методов, полей доступа и конструкторов – вот здесь и проявляется, та самая абстракция.
Рассмотрим некий класс:
class _Class { private: int Value; public: _Class() { } };
Внутри данный класс представляет собой структуру данных, которая не делится, ни на частные, ни на публичные поля доступа, а структура, в свою очередь, является «массивом» элементов разной величины. Т.е. ключевое слово struct просто говорит компилятору о том, что необходимо зарезервировать N байт памяти.
struct _Class { int Value; };
Значит, что адрес объекта класса или структуры – есть адрес первого атрибута. Получается, что для получения значения любого атрибута класса, необходимо иметь его адрес, который с легкость можно получить сдвигом указателя объекта класса на N >= 0 байт.
Например, поменяем значение частного атрибута класса _Class:
class _Class { private: int Value; public: _Class() { } }; _Class Object; __asm { lea eax, Object mov ebx, 123 mov [eax + 0], ebx }
lea eax, Object
– создаем указатель на объект класса и помещаем в регистр EAX.
mov [eax + 0], ebx
– помещаем в атрибут Value, значение регистра EBX = 123. (0 – байтовый сдвиг, соответственно для второго элемента он будет равен 4 т.к. sizeof(Value) = 4).
Вот таким вот, достаточно простым и понятным способом можно получать и менять значения всех атрибутов, не зависимо от принадлежности к полю доступа.
Получается, что все «запреты» в языке C++ происходят исключительно на уровне компилятора. Аналогичным образом, используя Inline Assembler, можно менять значения константных переменных, например:
const int Value = 7; __asm { mov ebx, 123 mov Value, ebx }
Методы
Но как же тогда устроены методы класса, если структура не хранит в себе указатели на функции?
Функции в классах работаю почти так же как и обычные функции. Именуются методы классов следующим образом _Class::_Class. Но основное отличие, разумеется, не в этом – это просто имя, а в 2 ключевых строках дизассемблированного кода.
1. Создается указатель на объект класса и помещается в регистр ECX
2. При вызове метода класса регистр ECX копируется в локальный указатель, именуемый this.
Пример полноценной реализации вызова метода класса на языке C++:
class _Class { private: int Value; public: _Class() { } public: void Function() { this->Value = 7; } }; _Class Object; Object.Function();
А вот как это выглядит на самом деле:
struct _Class { int Value; }; void _Class::_Class() { _Class* this; __asm { mov this, ecx } } void _Class::Function() { _Class* this; __asm { mov this, ecx } this->Value = 7; } _Class Object; __asm { lea ecx, Object call _Class::Function }
И о чем нам это говорит – независимо от поля, метод класса – это просто функция, которая при запуске инициализирует указатель на наш элемент, а класс — это просто структура с публичными атрибутами. Получается, что даже частные методы вызываются с помощью Inline Assembler, без каких либо запретов.
Вот и все, что я хотел рассказать. Спасибо за внимание!
ссылка на оригинал статьи http://habrahabr.ru/post/172237/
Добавить комментарий