C語言實現多態—模仿C++虛函數表


   在C++中,我們知道多態很大程度上依賴於虛函數,而虛函數的地址存放於虛函數表之中。運行期多態就是通過虛函數和虛函數表實現的。類的對象內部會有指向類內部的虛表地址的指針。通過這個指針調用虛函數。虛函數的調用會被編譯器轉換為對虛函數表的訪問。虛函數表就像一個地圖一樣,指明了實際所應該調用的函數。如果一個類有虛函數,那么這個類的所有對象共享一個虛函數表。

虛函數表的相關實現和怎么用虛函數實現多態的原理可以參考這個博主的文章,寫的很好理解

https://blog.csdn.net/haoel/article/details/1948051?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-4&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-4

虛函數表大致像下面這樣:

  • 根據C++ 虛函數表原理,我們可以考慮用C來嘗試,其實C++很大一部分是由C衍生過來的,所以我們下面 用一個例子來模擬一下。若用C語言來實現多態,可以利用"結構在內存中的布局與結構的聲明具有一致的順序"這一事實來實現繼承,再通過一個函數指針結構體來實現虛函數來實現多態。
  • struct Point
    {   
            int x, y;          
    }; 
    struct Shape   // 基類
    {   
            struct Methods* methods;   // 指向“虛函數表”
    };          
    struct Methods   // 將C++對應類中所有虛函數封裝到一個結構體里面
    {   
            float (*getPerimeter)(Shape * shape);          
            float (*getArea)(Shape * shape); 
            char* (*getShape)(Shape * shape);
    }; 
    /*Rectangle*/ 
    struct Rectangle
    {   
            struct Methods * methods;  //包含繼承Shape后的成員函數結構體的指針
            Point leftTop;    //Shape之外派生的成員變量 
            Point rightBottom;  //Shape之外派生的成員變量 
    };   
    float Rectangle_getPerimeter(Shape * shape)    // 計算矩形周長
    {   
            Rectangle * r = (Rectangle *) shape;   
            return (float)(r->rightBottom.x - r->leftTop.x + r->rightBottom.y - r->leftTop.y) * 2;   
    }   
    float Rectangle_getArea(Shape * shape)       // 計算矩形面積
    {
            Rectangle * r = (Rectangle *) shape; 
            return (float)(r->rightBottom.x - r->leftTop.x) * (r->rightBottom.y - r->leftTop.y);
    }
    char* Rectangle_getShape(Shape * shape)
    {
            return "Rectangle";
    }
    struct Methods rectangleMethods =   // 綁定Rectangle“類”實現的“虛函數”
    {
            &Rectangle_getPerimeter,   
            &Rectangle_getArea,
            &Rectangle_getShape
    }; 
    struct Circle
    {   
            struct Methods * methods;   
            Point center;   
            float radius;   
    };   
    float Circle_getPerimeter(Shape * shape)   // 計算圓周長
    {   
            Circle * c = (Circle *) shape;   
            return (float)2 * 3.14 * c->radius;     
    }   
    float Circle_getArea(Shape * shape)       // 計算圓面積
    {
            Circle * c = (Circle *) shape;   
            return (float)3.14*(c->radius)*(c->radius);   
    }
    char* Circle_getShape(Shape * shape)
    {
            return "Circle";
    }
    struct Methods circleMethods =     // 綁定Circle“類”實現的“虛函數”
    {   
            &Circle_getPerimeter,   
            &Circle_getArea,
            &Circle_getShape
    }; 
    /*main*/ 
    Shape* shapes[2];   // 基類指針數組
    Shape* new_rectangle(Point a, Point b)    // 創建Rectangle對象
    {   
            struct Rectangle* r = (Rectangle* )malloc(sizeof(Rectangle));   
            r->methods = &rectangleMethods;   
            r->leftTop = a;   
            r->rightBottom = b;   
            return (Shape*)r;   
    }   
    Shape* new_circle(Point a, float r)       // 創建Circle對象
    {   
            struct Circle* c = (Circle*)malloc(sizeof(Circle));   
            c->methods = &circleMethods;   
            c->center = a;
            c->radius = r;
            return (Shape*)c;   
    } 
    int main()
    {   
        Point c ={0,0};
        shapes[0] = new_circle(c,10);   
        Point a ={2,3};   
        Point b = {9,8};   
        shapes[1] = new_rectangle(a, b);   
        for (int i = 0; i < 2; i++)
            printf("%s's perimeter is: %f\n", (*shapes[i]->methods->getShape)(shapes[i]),
     (*shapes[i]->methods->getPerimeter)(shapes[i]));   
        for (int i = 0; i < 2; i++)   
            printf("%s's area is: %f\n", (*shapes[i]->methods->getShape)(shapes[i]),
    (*shapes[i]->methods->getArea)(shapes[i]));       
        getchar();   
        return 0;
    
  •  

     

     

 

 

 

 

 

 

 

 

   

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM