//polydemo.cpp --- by tsaiwn@csie.nctu.edu.tw // Demo how polymorphism works ? #include using namespace std; class aaa { long x; // 4 bytes public: aaa( ) { x=38; } long getX( ) { return x; } void cry( ) { cout << "aaa "; } // NOT virtual }; // 沒有 virtual 就不會 Polymorphism 多型 class BB: public aaa{ void cry( ) { cout << "BB "; } }; class CC: public aaa{ void cry( ) { cout << "CC "; } }; /// /// /// // the following has virtual function 注意 class bbb 只是有 virtual class bbb { long x; // 4 bytes public: bbb( ) { x=38; }; long getX( ) { return x; } virtual void cry( ) { cout << "bbb "; } //virtual; =0; ? }; // class bbb 除了 cry( )多 virtual 外完全相同於 class aaa class XXX: public bbb{ void cry( ) { cout << "XXX "; } }; class YYY: public bbb{ void cry( ) { cout << "YYY "; } }; aaa varaaa; // C++ 是物件, 若在 Java 則只是 reference (類似 pointer) bbb varbbb; aaa * pa; // C++ 的指標, Java 的 Reference 用起來與此相似 bbb * p22; int main( ) { cout << "sizeof varaaa =" << sizeof varaaa << endl; cout << "sizeof varbbb =" << sizeof varbbb << endl; pa = new BB( ); pa->cry( ); cout << endl; pa = new CC( ); pa->cry( ); cout << endl; // 沒polymorphism p22 = new XXX( ); p22->cry( ); cout << endl; p22 = new YYY( ); p22->cry( ); cout << endl; // 會Polymorphism return 0; // p22-> cry( ); 會依照 p22-> 指到的物件作 cry( ) }// int main( // 但 pa->cry( ); 永遠做 aaa::cry( ) /****** D:\COURSE\OOP\PPNT>g++ polydemo.cpp D:\COURSE\OOP\PPNT>a sizeof varaaa =4 <== 每個 aaa 物件用 4 bytes memory sizeof varbbb =8 <== 有 polymorphism 所以多一個有4bytes 的 pointer aaa aaa <== 注意各答案是誰印出的 ? XXX YYY <== 注意有 Polymorphism 喔 D:\COURSE\OOP\PPNT> g++ -S polydemo.cpp D:\COURSE\OOP\PPNT> type polydemo.s // 去掉不想看的 ... int main( ) { 123 .globl _main 124 .def _main; .scl 2; .type 32; .endef 125 _main: 126 pushl %ebp ;; save OLD eBP 127 movl %esp, %ebp ;; eBP = eSP; copy SP into BP 128 pushl %ebx 129 subl $20, %esp ;; eSP = eSP - 20; 130 andl $-16, %esp ;; so that esp mod 16 == 0 131 movl $0, %eax ;; eAX = 0; 132 addl $15, %eax 133 addl $15, %eax ;; now eAX is 30 134 shrl $4, %eax 135 sall $4, %eax ;; now eAX is 16 136 movl %eax, -8(%ebp) 137 movl -8(%ebp), %eax 138 call __alloca ;; 要一些記憶體, 16 bytes ? 139 call ___main ;; do some housekeeping jobs ;;; cout << "sizeof varaaa =" << sizeof varaaa << endl; 140 movl $LC0, 4(%esp) 141 movl $__ZSt4cout, (%esp) 142 call __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc 143 movl $4, 4(%esp) 144 movl %eax, (%esp) 145 call __ZNSolsEj 146 movl $__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, 4(%esp) 147 movl %eax, (%esp) 148 call __ZNSolsEPFRSoS_E ;;; cout << "sizeof varbbb =" << sizeof varbbb << endl; 149 movl $LC1, 4(%esp) 150 movl $__ZSt4cout, (%esp) 151 call __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc 152 movl $8, 4(%esp) 153 movl %eax, (%esp) 154 call __ZNSolsEj 155 movl $__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, 4(%esp) 156 movl %eax, (%esp) 157 call __ZNSolsEPFRSoS_E 158 movl $4, (%esp) 159 call __Znwj 160 movl %eax, %ebx 161 movl %ebx, (%esp) 162 call __ZN2BBC1Ev ;;; pa = new BB( ); 158 movl $4, (%esp) ;; 參數 (long)4 159 call __Znwj ;; .. 以便要 4 bytes memory 160 movl %eax, %ebx ;; 要到的 address 起點 161 movl %ebx, (%esp) 162 call __ZN2BBC1Ev ;; 叫用 BB 的constructor 163 L11: 164 movl %ebx, %eax 165 movl %eax, _pa ;; 存入 pa ;;; pa->cry( ); ;; 因為沒有 virtual; 用 static binding就可 166 movl _pa, %eax 167 movl %eax, (%esp) 168 call __ZN3aaa3cryEv ;; 寫死的 aaa::cry( ), static binding ;;; cout << endl; 169 movl $__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, 4(%esp) 170 movl $__ZSt4cout, (%esp) 171 call __ZNSolsEPFRSoS_E //... ;;; p22 = new XXX( ); // 注意 bbb內有virtual cry, Polymorphism 喔! 186 movl $8, (%esp) ;; 參數 (long)8 187 call __Znwj ;; .. 以便要 8 bytes memory 188 movl %eax, %ebx 189 movl %ebx, (%esp) ;; 把物件的 address 放堆疊內 190 call __ZN3XXXC1Ev ;; 叫用 Constructor XXX( ) 191 L15: 192 movl %ebx, %eax 193 movl %eax, _p22 ;; 記得把要到的memory起點放 p22 ;;; p22->cry( ); //注意bbb::cry( )是 virtual, 所以 Polymorphism 194 movl _p22, %eax ;; p22 內容是 pointer, 抓到 eAX 195 movl (%eax), %edx ;; eDX = memory[eAX]; 196 movl _p22, %eax 197 movl %eax, (%esp) ;; 把 p22-> 物件起點推入stack 以便給 cry用 198 movl (%edx), %eax ;; eDX 指過去處放著正確 cry( ) 函數 address 199 call *%eax ;; 抓來eAX 並 indirect call , dynamic binding ;;; cout << endl; 200 movl $__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, 4(%esp) 201 movl $__ZSt4cout, (%esp) 202 call __ZNSolsEPFRSoS_E ;;; p22 = new YYY( ); //注意bbb::cry( )是 virtual, 所以 Polymorphism 203 movl $8, (%esp) 204 call __Znwj ;; 去要 8 bytes 205 movl %eax, %ebx ;; 要到的memory起點 address .. 206 movl %ebx, (%esp) ;; ..推入 stack 207 call __ZN3YYYC1Ev ;; 然後叫用constructor YYY( ) 208 L17: 209 movl %ebx, %eax 210 movl %eax, _p22 ;; 當然也要把起點 address 放入 p22 ;;; p22->cry( ); ;; 準備把該正確 cry( ) 起點address 抓入 eax 211 movl _p22, %eax ;; p22 內容是 pointer, 抓到 eAX 212 movl (%eax), %edx ;; eDX = memory[eAX]; 213 movl _p22, %eax 214 movl %eax, (%esp) ;; 把 p22-> 物件起點推入stack 以便給 cry用 215 movl (%edx), %eax ;; eDX 指過去處放著正確 cry( ) 函數 address 216 call *%eax ;; indirect call , dynamic binding ;;; cout << endl; ;; 以下是 constructor YYY( ) 228 .globl __ZN3YYYC1Ev 229 .def __ZN3YYYC1Ev; .scl 2; .type 32; .endef 230 __ZN3YYYC1Ev: 231 pushl %ebp ;; save OLD BP 232 movl %esp, %ebp ;; copy SP into BP 233 subl $8, %esp ;; ESP = ESP - 8; 挪出 8 bytes 空間 234 movl 8(%ebp), %eax ;; 物件的 address 235 movl %eax, (%esp) ;; 推入堆疊以便叫用老爸的 constructor 236 call __ZN3bbbC2Ev ;; 叫用 super class 的 constructor 237 movl 8(%ebp), %eax ;; 取得物件要用的要放 cry( )起點之address 238 movl $__ZTV3YYY+8, (%eax) ;; 這是 YYY::cry( )的 address 239 leave 240 ret ;; 以下是 constructor bbb( ) 但是給 bbb 的繼承者用的 ;; bbb( ) { x=38; }; 244 .globl __ZN3bbbC2Ev 245 .def __ZN3bbbC2Ev; .scl 2; .type 32; .endef 246 __ZN3bbbC2Ev: 247 pushl %ebp 248 movl %esp, %ebp 249 movl 8(%ebp), %eax 250 movl $__ZTV3bbb+8, (%eax) ;; 這是 bbb::cry( ) 的位址 251 movl 8(%ebp), %eax 252 movl $38, 4(%eax) ;; bbb( ) { x=38; }; 253 popl %ebp 254 ret ;; 以下是 constructor XXX( ) 258 .globl __ZN3XXXC1Ev 259 .def __ZN3XXXC1Ev; .scl 2; .type 32; .endef 260 __ZN3XXXC1Ev: 261 pushl %ebp ;; save OLD BP 262 movl %esp, %ebp ;; copy SP into BP 263 subl $8, %esp ;; ESP = ESP - 8; 挪出 8 bytes 空間 264 movl 8(%ebp), %eax ;; 物件的 address 265 movl %eax, (%esp) ;; 推入堆疊以便叫用老爸的 constructor 266 call __ZN3bbbC2Ev ;; 叫用 super class 的 constructor 267 movl 8(%ebp), %eax ;; 取得物件要用的要放 cry( )起點之address 268 movl $__ZTV3XXX+8, (%eax) ;; 這是 XXX::cry( ) 的 address 269 leave ;; 上列是把該 address 存到 p->cry( ) 可用 270 ret ;; 以下是 constructor CC( ) 274 .globl __ZN2CCC1Ev 275 .def __ZN2CCC1Ev; .scl 2; .type 32; .endef 276 __ZN2CCC1Ev: 277 pushl %ebp 278 movl %esp, %ebp 279 subl $8, %esp 280 movl 8(%ebp), %eax ;; this 物件的 address 281 movl %eax, (%esp) ;; 推入堆疊內以便叫用老爸的 constructor 282 call __ZN3aaaC2Ev ;; 叫用 super class 的 constructor 283 leave ;; 沒有virtual function就沒有 Polymorphism 284 ret ;; 以下是 constructor aaa( ) 但給 aaa 繼承者用的 288 .globl __ZN3aaaC2Ev 289 .def __ZN3aaaC2Ev; .scl 2; .type 32; .endef 290 __ZN3aaaC2Ev: 291 pushl %ebp 292 movl %esp, %ebp 293 movl 8(%ebp), %eax 294 movl $38, (%eax) 295 popl %ebp 296 ret ;; 以下是 constructor BB( ) 317 .globl __ZN2BBC1Ev 318 .def __ZN2BBC1Ev; .scl 2; .type 32; .endef 319 __ZN2BBC1Ev: 320 pushl %ebp 321 movl %esp, %ebp 322 subl $8, %esp 323 movl 8(%ebp), %eax 324 movl %eax, (%esp) 325 call __ZN3aaaC2Ev ;; 叫用 super class 的 constructor 326 leave ;; 沒有virtual function就沒有 Polymorphism 327 ret ;; 以下是一個 table for YYY 物件 355 .align 8 356 __ZTV3YYY: 357 .long 0 358 .long __ZTI3YYY 359 .long __ZN3YYY3cryEv ;; 偷放 YYY::cry( ) 的 address ;; 以下是一個 table for XXX 物件 363 .align 8 364 __ZTV3XXX: 365 .long 0 366 .long __ZTI3XXX 367 .long __ZN3XXX3cryEv ;; 偷放 XXX::cry( ) 的 address ;; 以下是一個 table for bbb 物件 371 .align 8 372 __ZTV3bbb: 373 .long 0 374 .long __ZTI3bbb 375 .long __ZN3bbb3cryEv ;; 偷放 bbb::cry( ) 的 address ;;;;;;;; ;;; 因為 class aaa 內沒有任何 virtual function, ;;; .. 所以不必準備 tables for Object of class aaa, BB, CC ;; 以下是 constructor bbb( ) ;; bbb( ) { x=38; }; 416 .globl __ZN3bbbC1Ev 417 .def __ZN3bbbC1Ev; .scl 2; .type 32; .endef 418 __ZN3bbbC1Ev: 419 pushl %ebp 420 movl %esp, %ebp 421 movl 8(%ebp), %eax 422 movl $__ZTV3bbb+8, (%eax) ;; bbb::try( ) 的 address 423 movl 8(%ebp), %eax 424 movl $38, 4(%eax) 425 popl %ebp 426 ret ;; 以下是 constructor aaa( ) ;;; aaa( ) { x=38; } 430 .globl __ZN3aaaC1Ev 431 .def __ZN3aaaC1Ev; .scl 2; .type 32; .endef 432 __ZN3aaaC1Ev: 433 pushl %ebp 434 movl %esp, %ebp 435 movl 8(%ebp), %eax 436 movl $38, (%eax) 437 popl %ebp 438 ret ;;; ;; 沒有virtual function就沒有 Polymorphism D:\COURSE\OOP\PPNT> //////////////////////////////////////////////////// // 修改 #include 去掉 using namespace 以便用 TCC D:\COURSE\OOP\PPNT> tcc -S polydemo.cpp D:\COURSE\OOP\PPNT> type polydemo.asm // 去掉不想看的 ... 99 ; pa = new BB( ); ; 此 BB 沒有 Polymorphism 100 ; 101 mov ax,4 ;; 4 要 push 到堆疊 102 push ax 103 call near ptr @$bnew$qui ;; 去要 4 bytes 104 pop cx 105 mov word ptr [bp-6],ax 106 or ax,ax 107 je short @1@86 108 mov bx,word ptr [bp-6] 109 mov word ptr [bx+2],0 110 mov word ptr [bx],38 111 mov ax,word ptr [bp-6] 112 jmp short @1@114 113 @1@86: 114 mov ax,word ptr [bp-6] 115 @1@114: 116 mov word ptr DGROUP:_pa,ax 117 ; 118 ; pa->cry( ); 119 ; 120 xor ax,ax 121 push ax 122 mov ax,offset DGROUP:s@+32 123 push ax 124 mov ax,offset DGROUP:_cout 125 push ax 126 call near ptr @ostream@outstr$qpxzct1 127 add sp,6 128 ; ///... 173 ; p22 = new XXX( ); 174 ; 175 mov ax,6 ;; 6 要 push 到堆疊 176 push ax 177 call near ptr @$bnew$qui ;; 去要 6 bytes 178 pop cx 179 mov si,ax 180 or ax,ax 181 je short @1@254 182 mov word ptr [si],offset @@bbb@ ;; bbb::cry( ) 的address 183 mov word ptr [si+4],0 ;; 上句白做的, 因 Line 185 184 mov word ptr [si+2],38 185 mov word ptr [si],offset @@XXX@ ;; XXX::cry( ) 的address 186 mov ax,si 187 jmp short @1@282 188 @1@254: 189 mov ax,si 190 @1@282: 191 mov word ptr DGROUP:_p22,ax 192 ; 193 ; p22->cry( ); 194 ; 195 push word ptr DGROUP:_p22 196 mov bx,word ptr DGROUP:_p22 197 mov bx,word ptr [bx] ;; 抓到正確的 cry( )之address 198 call word ptr [bx] ;;; indirect call 199 pop cx 200 ; // ... 207 ; 208 ; p22 = new YYY( ); 209 ; 210 mov ax,6 211 push ax 212 call near ptr @$bnew$qui 213 pop cx 214 mov di,ax 215 or ax,ax 216 je short @1@338 217 mov word ptr [di],offset @@bbb@ ;; bbb::cry( ) 的address 218 mov word ptr [di+4],0 219 mov word ptr [di+2],38 220 mov word ptr [di],offset @@YYY@ ;; YYY::cry( ) 的address 221 mov ax,di 222 jmp short @1@366 223 @1@338: 224 mov ax,di 225 @1@366: 226 mov word ptr DGROUP:_p22,ax 227 ; 228 ; p22->cry( ); 229 ; 230 push word ptr DGROUP:_p22 231 mov bx,word ptr DGROUP:_p22 232 mov bx,word ptr [bx] 233 call word ptr [bx] 234 pop cx 235 ; 236 ; cout << endl; 237 ; 238 mov ax,offset DGROUP:_cout 239 push ax 240 call near ptr @endl$qr7ostream 241 pop cx 242 ; 243 ; return 0; 244 ; 245 xor ax,ax 246 jmp short @1@394 247 @1@394: 248 ; 249 ; }// int main( 250 ; 251 pop di 252 pop si 253 mov sp,bp 254 pop bp 255 ret //... 277 _DATA segment word public 'DATA' 278 @YYY@ segment virtual 279 @@YYY@ label byte 280 dw @@YYY@cry$qv ;; 偷存 YYY::cry( ) 的address 281 @YYY@ ends 282 _DATA ends 283 _DATA segment word public 'DATA' 284 @bbb@ segment virtual 285 @@bbb@ label byte 286 dw @@bbb@cry$qv ;; 偷存 bbb::cry( ) 的address 287 @bbb@ ends 288 _DATA ends 289 _DATA segment word public 'DATA' 290 @XXX@ segment virtual 291 @@XXX@ label byte 292 dw @@XXX@cry$qv ;; 偷存 XXX::cry( ) 的address 293 @XXX@ ends 294 _DATA ends D:\COURSE\OOP\PPNT> ***********************/