--- tags: 研讀筆記, polymorphism, C --- # 研讀筆記: Achieving polymorphism in C [source](https://www.codeproject.com/Articles/739687/Achieving-polymorphism-in-C) > 全文主要是翻譯原文,並記錄自己的閱讀。 ### Introduction 在這篇文章中我們的目的是建構一個指向 Shape 物件的指標陣列,每一個 Shape 指向物件 Circle、Square、Goat 等不同物件,皆以純 C 實現。 ### Airticle 先建立三個結構。 ```cpp= struct Square{ int width; int height; }; struct Circle{ float radius; }; struct Triangle{ int base; int height; }; ``` 印出來看看。 ```cpp= printf("size of square is %d\n", sizeof(struct Square)); printf("size of Circle is %d\n", sizeof(struct Circle)); printf("size of Triangle is %d\n", sizeof(struct Triangle)); ``` 結果如下。 ```cpp= //size of cube is 8 //size of circle is 4 //size of triangle is 8 ``` 可以注意到這邊的結構中的成員在記憶體中很巧妙的對齊。 ![](https://i.imgur.com/PCwnTEt.png) 以下的輸出結果是 1,印出了 Square 前面 4 個 bytes 的值。 ```cpp= struct Square square; square.width = 1; square.height = 2; printf("the first 4 bytes of square are %d", (*(int*)(&square))) ``` 以下這個也是印出 1。 ```cpp= printf("the first 4 bytes of square are %d\n", square); //this works? ``` operand `&` 會給我們 square 這個結構一開始的記憶體位置,這個也能印出來,但是我們要把她轉成 int 的地址。 ```cpp= printf("the first 4 bytes of square are %d\n", &square); ``` 這樣我們就可以印出來 int 用我們轉乘的 int 指標。 ```cpp= printf("the first 4 bytes of square are %d\n", (int*)&square); ``` 這個也就是我們第一個範例做的事情,但是在我們印出來之前,可以選擇要移動指標。 ```cpp= printf("the second 4 bytes of square are %d\n", (*(int*)&square + 1)); ``` 接下來是 function pointers,C 對待 function pointer 就跟其他 member 一樣。底下是一個函式不用吃任何參數也不用回傳值。 ```cpp= void print_square( void ){ printf("Hello Square\n"); } ``` function pointer 宣告在結構當中。 ```cpp= struct Square{ int width; int height; //now for functions void (* print)( void ); float (* area)( struct Square * this ); }; ``` 就像剛剛提到的,我們要建立 Square 需要手動去設定每個成員對應的值,會需要 constructor。 ```cpp= void init_square( struct Square * square, int w, int h ){ (*square).print = print_square; (*square).width = w; (*square).height = h; (*square).area = calc_square_area; } ``` 所有結構都要宣告。 ```cpp= struct Square square; struct Circle circle; struct Triangle triangle; init_square( &square, 2, 2 ); init_circle( &circle, 7.7 ); init_triangle( &triangle, 2, 3 ); square.print(); circle.print(); triangle.print(); printf("the area of the square is %f\n", square.area(&square)); printf("the area of the circle is %f\n", circle.area(&circle)); printf("the area of the triangle is %f\n", triangle.area(&triangle)); ``` 接下來,讓我們來建立結構 Shape,讓三個物件繼承他。 ```cpp= //abstract father class struct Shape{ void (* print)( void ); float (* area)( struct Shape * this ); }; ``` 如果幫剛剛的 Square 改成用 Shape 呼叫 print 函式,會發生甚麼事呢? ```cpp= struct Shape * pointer = (struct Shape *)&square; (*pointer).print(); //?? what is going to happen?? ``` 會得到 segmentation fault。那我們再回來討論這些結構的記憶體。 ![](https://i.imgur.com/Vs5QadC.png) 在 Shape 當中的 print 函式是在最開頭的 4 bytes,但是在 Square 當中是第三個 4 bytes。 當我們把一個指向 Square 指標轉成指向 Shape 的指標,記憶體是不變的(untouched),記住,轉型通常是不會改變內部記憶體的任何東西,除非是依些 dynamic casting。 現在讓我們來解決這個問題,填滿前面 8 bytes。 ```cpp= struct Shape{ char padd[8]; void (* print)( void ); float (* area)( struct Shape * this ); }; ``` 最後。 ```cpp= struct Shape * shapes[3]; shapes[0] = (struct Shape *)&square; shapes[1] = (struct Shape *)&circle; shapes[2] = (struct Shape *)&triangle; int i; for(i=0; i<3; ++i){ (*shapes[i]).print(); printf("%f\n\n", (*shapes[i]).area(shapes[i])); } ``` 這就是很基本的用 OOP 觀念實作多形(polymorphism)。