本文適合會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語言實現基礎的設計模式(比如工廠模式),不然你學面向對象幹嘛呢。