c語言實現面向對象的基礎思想

本文適合會C語言,並瞭解面向對面思想的同志。如果連多態還 不瞭解的話,就可以不用看了。同時我也覺的,用 c  學會面向對象編程,不是爲了 真正寫出程序(畢竟高級語言纔是爲這個而生的,且編譯器會進行優化,除開內存管理的話,內存佔用不一定比C更大),而是讓我們對高級語言的底層實現 更加了解。

代碼爲隨手打的,看思路爲緊,下面說的類就是結構體

面向對象三要素:封裝,繼承,多態,

1:先說封裝

:假設A類有成員變量int  ,成員函數void fun(), 沒有實現的成員函數 void fun2(),相當於一個虛函數

實現就是struct而已,成員函數用函數指針實現.

 

typedef struct A{

    int x;

    void (*fun)();
    
    void (*fun2)();  //在這個代碼段沒有給這個函數指針賦值(看A_init函數),相當於這是一個虛函數。

}A;

void fun(A *this) 

{

printf("調用A類成員變量X = %d\n",this->x);

}

類A的init函數 ,可以理解爲構造函數

void A_init( A* this )

{

    this->x = 0;

    this->fun = &fun();

}

 

封裝還是很簡單的。

2:繼承

假設有上面的A類, B類繼承至A類,則在B類中創建 A類成員(實際是struct 成員)就行了,同時實現類A未實現的函數fun2,可以理解爲實現虛函數.

typedef struct B

{

    int  y;   
    struct A  a; //建議聲明在開頭,這裏是爲了體現offsetof 的妙用
}B; 

void fun2(A *this) 
{
    
    printf(“實現父類A的虛函數fun2 成員變量X=%d\n”,this->x);

}

void B_init(Struct B * this) //可以理解爲B的構造函數
{
    A_init(& this->a );  //調用A的構造函數
    this->y = 0;
    this->a.fun2 = fun2;  //實現類A的虛函數
}

沒錯,上面就是繼承,不然你以爲有多複雜嘛

 

3:多態

多態的實現就是子類向上轉型,和父類的向下轉型(通過上面的虛函數,你應該有一點想法了吧),這裏要介紹宏 offsetof. 當然也可以自己實現這個宏:  作用僅僅是算出結構體成員相對結構體開頭的偏移量。

#define offsetof(TYPE,MEMBER)   (size_t)&((TYPE *)0)->MEMBER)

也可以用container_of替換,詳細請移步:https://blog.csdn.net/GouplovXim1314/article/details/80445287

3.1  向上轉型 

當你向上或向下轉型時,說明你已經實現了基類A的虛函數fun2(),不同的子類有不同的實現,實質是函數指針指向的函數不同.



//在上面繼承出,我們並沒有對虛函數fun2()做該改變(向下轉型),這使得我們無法使用類B的變量y,所以我們在這裏重新構建類B,讓大家看到多態的威力。

//以下代碼與上面的代碼無關
//**********************************類A 

typedef struct A{

    int x;

    void (*fun)();
    
    void (*fun2)();  //在這個代碼段沒有給這個函數指針賦值(看A_init函數),相當於這是一個虛函數。

}A;

void fun(A *this) 

{

printf("調用A類成員變量X = %d\n",this->x);

}

類A的init函數 ,可以理解爲構造函數

void A_init( A* this )

{

    this->x = 0;

    this->fun = &fun();

}
//************************************類A結束
//************************************類B,繼承至類A
typedef struct B

{
    int  y;   
    struct A  a; //建議聲明在開頭,這裏是爲了體現offsetof 的妙用
}B; 

void B_fun2(A *this)  //對虛虛函數有改變
{
    //向下轉型到B,
    //請大家思考:爲什麼先把this轉爲char *,
    B *b_p = (B *)(char *)this - offsetof(B,a); 

    //在這麼,我就可以調用B的函數和變量了
    printf(“實現父類A的虛函數fun2  A成員變量X=%d,B成員變量Y= %d\n”,this->x,b_p->y);

}

void B_init(Struct B * this) //可以理解爲B的構造函數
{
    A_init(& this->a );  //調用A的構造函數
    this->y = 0;
    this->a.fun2 = B_fun2;  //實現類A的虛函數
}
//**************************************類B結束
//************************************類C,繼承至類A
typedef struct C

{
    int  Z;   
    struct A  a; //建議聲明在開頭,這裏是爲了體現offsetof 的妙用
}C; 

void C_fun2(A *this)  //對虛虛函數有改變
{
    //向下轉型到C,
    //請大家思考:爲什麼先把this轉爲char *,
    C *C_p = (C *)(char *)this - offsetof(C,a); 

    //在這麼,我就可以調用C的函數和變量了
    printf(“實現父類A的虛函數fun2  A成員變量X=%d,C成員變量Z= %d\n”,this->x,C_p->Z);

}

void C_init(Struct C * this) //可以理解爲B的構造函數
{
    A_init(& this->a );  //調用A的構造函數
    this->Z = 0;
    this->a.fun2 = C_fun2;  //實現類A的虛函數
}
//**************************************類B結束

//感受多態的威力吧

int main()
{
    struct B b;
    B_init( &b );  //在B的 init函數中會調用 A 的init函數
    struct C c;
    C_init(&c);

    //向上轉型
    struct A *a_p = (Struct A *)offsetof(B,a);
    a_p->fun2();  //調用的是B_fun2;

    a_p = (Struct A *)offsetof(C,a);
    a_p->fun2();  //調用的是C_fun2;
   
    return 0;
}

 

以上爲大概思路,建議大家看看LW_OOPC 庫,簡單又有技巧,對於深入理解 C 和 面向對象高級語言的底層原理  有幫助。

 

不過 c 語言實現面向對面還是挺難的(代碼不難,邏輯有點難), 面向對象的要求就是爲了複用,而複用的結晶就是設計模式。  比如我的類繼承於接口,而類中又有多個接口成員時,就要區分自生繼承的接口與接口成員。但缺點也是優點,讓我對高級語言的實現有了深刻的理解。

 

假設你已經會了多態,那麼進一步就是用C語言實現基礎的設計模式(比如工廠模式),不然你學面向對象幹嘛呢。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章