На этом шаге мы рассмотрим представление объектно-ориентированной программы.
Объектно-ориентированное программирование в значительной степени усложняет получаемый исполняемый код. Рассмотрим один простой пример. В следующем листинге представлена программа на C++.
#include <stdio.h> #include <string.h> #include <conio.h> class stroka { public: char c[200]; char g[100]; stroka() {strcpy(g,"Privet");} int strcp(); }; stroka::strcp() { strcpy(c,g); return 0; } void main() { stroka *s = new stroka; s->strcp(); printf("%s\n",s->c); delete s; getch(); }
Ниже представлен дизассемблированный код функции main.
CODE:0041008E _main proc near ; DATA XREF: DATA:00420044o CODE:0041008E CODE:0041008E var_24 = dword ptr -24h CODE:0041008E var_14 = word ptr -14h CODE:0041008E dest = dword ptr -4 CODE:0041008E argc = dword ptr 8 CODE:0041008E argv = dword ptr 0Ch CODE:0041008E envp = dword ptr 10h CODE:0041008E CODE:0041008E push ebp CODE:0041008F mov ebp, esp CODE:00410091 add esp, 0FFFFFFDCh CODE:00410094 push ebx CODE:00410095 mov eax, offset unk_420080 CODE:0041009A call @__InitExceptBlock CODE:0041009F push 12Ch CODE:004100A4 call @$bnew$qui ; operator new(uint) CODE:004100A9 pop ecx CODE:004100AA mov [ebp+dest], eax CODE:004100AD test eax, eax CODE:004100AF jz short loc_4100D8 CODE:004100B1 mov [ebp+var_14], 14h CODE:004100B7 push offset aPrivet ; src CODE:004100BC mov eax, [ebp+dest] CODE:004100BF add eax, 0C8h CODE:004100C4 push eax ; dest CODE:004100C5 call _strcpy CODE:004100CA add esp, 8 CODE:004100CD mov [ebp+var_14], 8 CODE:004100D3 mov ebx, [ebp+dest] CODE:004100D6 jmp short loc_4100DB CODE:004100D8 ; ------------------------------------------------------------ CODE:004100D8 CODE:004100D8 loc_4100D8: ; CODE XREF: _main+21j CODE:004100D8 mov ebx, [ebp+dest] CODE:004100DB CODE:004100DB loc_4100DB: ; CODE XREF: _main+48j CODE:004100DB push ebx ; dest CODE:004100DC call sub_410074 CODE:004100E1 pop ecx CODE:004100E2 push ebx CODE:004100E3 push offset aS ; format CODE:004100E8 call _printf CODE:004100ED add esp, 8 CODE:004100F0 push ebx CODE:004100F1 call @$bdele$qpv ; operator delete(void *) CODE:004100F6 pop ecx CODE:004100F7 call _getch CODE:004100FC mov eax, [ebp+var_24] CODE:004100FF mov large fs:0, eax CODE:00410105 pop ebx CODE:00410106 mov esp, ebp CODE:00410108 pop ebp CODE:00410109 retn CODE:00410109 _main endp
Как видите, получившийся код достаточно сложен. Мы ее будем проводить его детальный анализ, т.к. в этом случае нам пришлось бы включать в него и анализ библиотечных программ. Остановимся только на некоторых ключевых моментах.
Оператор new сводится к выполнению библиотечной процедуры: @$bnew$qui. Она выделяет память для свойств экземпляра объекта (300 байт = 12CH).
Конструктор хранится и выполняется непосредственно в теле программы. Это связано с тем, что и сам конструктор определен в тексте класса. Для эксперимента попробуйте вынести текст конструктора в отдельную функцию, и вы увидите, что конструктор будет вызываться из main, как обычная функция.
Процедуру @__InitExceptBlock вставляет транслятор для поддержки обработки исключений (Exception). Вы можете удалить информацию, необходимую для обработки исключений, тем самым несколько сократив размер исполняемой программы, но тогда вы не сможете использовать операторы исключений, такие как try или catch.
При вызове какого-либо метода в стек всегда помещается, по крайней мере, один параметр - указатель на экземпляр объекта.
Приведем теперь фрагмент той же дизассемблированной программы, но с использованием опции -х, что для транслятора Borland C++ означает - не поддерживать обработку исключений. Как видите, текст программы оказался значительно проще.
CODE:0041008E _main proc near ; DATA XREF: DATA:00420044o CODE:0041008E CODE:0041008E argc = dword ptr 8 CODE:0041008E argv = dword ptr 0Ch CODE:0041008E envp = dword ptr 10h CODE:0041008E CODE:0041008E push ebp CODE:0041008F mov ebp, esp CODE:00410091 push ebx CODE:00410092 push 12Ch CODE:00410097 call @$bnew$qui ; operator new(uint) CODE:0041009C pop ecx CODE:0041009D mov ebx, eax CODE:0041009F test ebx, ebx CODE:004100A1 jz short loc_4100BB CODE:004100A3 push offset aPrivet ; src CODE:004100A8 lea eax, [ebx+0C8h] CODE:004100AE push eax ; dest CODE:004100AF call _strcpy CODE:004100B4 add esp, 8 CODE:004100B7 mov eax, ebx CODE:004100B9 jmp short loc_4100BD CODE:004100BB ; ------------------------------------------------------------ CODE:004100BB CODE:004100BB loc_4100BB: ; CODE XREF: _main+13j CODE:004100BB mov eax, ebx CODE:004100BD CODE:004100BD loc_4100BD: ; CODE XREF: _main+2Bj CODE:004100BD mov ebx, eax CODE:004100BF push ebx ; dest CODE:004100C0 call sub_410074 CODE:004100C5 pop ecx CODE:004100C6 push ebx CODE:004100C7 push offset aS ; format CODE:004100CC call _printf CODE:004100D1 add esp, 8 CODE:004100D4 push ebx CODE:004100D5 call @$bdele$qpv ; operator delete(void *) CODE:004100DA pop ecx CODE:004100DB call _getch CODE:004100E0 pop ebx CODE:004100E1 pop ebp CODE:004100E2 retn CODE:004100E2 _main endp
На следующем шаге мы приведем пример приложения, позволяющего изменять цвет фона окна.