用 C 语言实现面向对象编程
一、類的封裝實現:借用高煥堂的宏頭文件,類很容易封裝為如下的格式
1、類的定義,其中 CLASS() 是 lw_oopc_kc.h 中定義的宏
| #include "lw_oopc_kc.h" CLASS(A) { int a; void(*init)(void*,int); void(*put)(void*); }; |
2、成員函數的實現
類的封裝實質是用借用 struct 結構體,用函數指針來表示 C++中類的方法(成員函數)。接下來給類 A 的方法寫實體函數。
| void init_A(void *t,int x) { A *cthis = (A*)t; cthis->a = x; } void put_A(void*t) { A *cthis = (A*)t; printf(" %d ",cthis->a); } |
3、類(結構體)中的函數指針與實現函數的關聯 通過下面的宏把類的方法(函數指針)和實現函數關聯:
| CTOR(A) FUNCTION_SETTING (init, init_A); FUNCTION_SETTING (put, put_A); END_CTOR |
4、對象的定義、構造和初始化
如果沒有這個連接處理,類(實際是 struct)中的函數指針就沒有函數的功能。函數 init_A()是 XXX_A() 的命名模式,是指明 XXX_A()屬于 A 類的函數,方便程序的理解和維護。下面就是要構造 類。在 C++中這個工作系統自動調用構造函數實現而在 C 中,這個過程智能顯示調用來實現。借助 lw_oopc_kc.h (或"lw_oopc.h")可以利用宏CLASS_CTOR(class,obj)來將定義的對象進行構造,使之 有數據的同時有方法的功能。實例化一個對象 3 步子如下:
| A aa1; // 1、定義對象 CLASS_CTOR(A,aa1); // 2、構造對象—使得函數指針和函數關聯 aa1.init(&aa1, 10); // 3、初始化對象的成員變量,注意要: &aa1(取地址) |
二、C 繼承的實現:
1、子類的定義:在類的開頭借用已經定義的類進行定義一個變量,為了更加明白,表明是繼承,增加一個宏定義:
| CLASS(B) { INHERIT(A); // 繼承 A 類 int b; // 子類的成員 void (*init) (void*, int x); void (*put) (void*); }; |
于是以類 B 繼承類 A 為例子如下:
| void init_B (void*t, int x, int y) { B *cthis = (B*) t; CLASS_CTOR(A, cthis->A); //----繼承的基類在這里構造,對象是 cthis->A cthis->A.init(&cthis->A, x);//----繼承的基類的初始化, 注意:&cthis->A cthis->b = y; } void put_B (void *t) { B *cthis = (B*) t; cthis->A.put (&cthis->A); //---子類調用父類的方式 printf(" %d ",cthis->b); //---輸出類成員值 } |
2、子類的成員函數實現,為了方便辨別,類 B 的成員函數帶后綴 ‘_B’
| int main() { A aa1; B b; CLASS_CTOR(A,aa1); //--構造 aa1 對象 aa1.init(&aa1,5); //--初始化 aa1 對象aa1.put(&aa1); //--調用 aa1 對象的成員函數 CLASS_CTOR(B, b); //---構造 b 對象 b.init(&b,100,78); //--初始化 b 對象,包括基類 A 的構造和初始化 b.put(&b); //--調用 b 對象成員函數 b.A.put(&b.A); //--調用 b 對象的基類成員函數 return 0; } |
3、子類的構造函數,和無繼承類一樣,將函數指針和函數關聯
| CTOR(B) FUNCTION_SETTING (init, init_B); //---函數指針和函數關聯的宏 FUNCTION_SETTING (put, put_B); END_CTOR |
說明:對基類的構造,不能在子類的構造宏 CTOR(B) 和 END_CTOR 之間進行,因為那時候子類 B 沒有實例化,故沒有實體對象,CLASS_CTOR(A, cthis->A);不能放在里面,只好放在 init_B() 函數里面,因為那時候 B 類已經有實例化對象了。這樣的做法與 C++的做法不一樣。C++在構造 B 的對象時,先調用 A 類的構造函數實例化對象中的基類部分。下面為main()函數的調用處理:
| int main() { A aa1; B b; CLASS_CTOR(A,aa1); //--構造 aa1 對象 aa1.init(&aa1,5); //--初始化 aa1 對象aa1.put(&aa1); //--調用 aa1 對象的成員函數 CLASS_CTOR(B, b); //---構造 b 對象 b.init(&b,100,78); //--初始化 b 對象,包括基類 A 的構造和初始化 b.put(&b); //--調用 b 對象成員函數 b.A.put(&b.A); //--調用 b 對象的基類成員函數 return 0; } |
輸出結果為:5 100 78 100
三、多態的實現:
多態,簡而言之即一個接口,多種實現。也就是用相同的抽象類的代碼實現不同 的功能。在 C 中多態的實現是通過接口來實現的。借用 lw_oopc.h 宏文件,設計一個計算的多態例子如下:
1、接口的定義:本例是實現加法、減法運算。
加和減都是調用類的同一個成員函數,卻分別實現 了加、減的功能。本例的接口表示獲得計算結果,但不知道采樣什么樣的計算方法。
| /* operater.h */ #ifndef OPER_H #define OPER_H INTERFACE(IOPERATOR) { double (*GetResult)(double,double); }; #endif /*-------------end of operater.h ------------------*/ |
2、 在加法類 Add 中實現接口 IOPERATOR
| /***** Add.C ***/ #include "lw_oopc_kc.h" #include"operater.h" // 頭文件順序很重要,lw_oopc_kc.h 在前,原因很簡單不解釋 #include "classes.h" /************************** 類 Add 定義在 classes.h 中 CLASS(Add) { IMPLEMENTS(IOPERATOR); };************/ static double GetResult(double a,double b) { return (a+b); } CTOR(Add) FUNCTION_SETTING(IOPERATOR.GetResult,GetResult); END_CTOR /***----- END OF ADD.C-----****/ |
3、 在減法類 Sub 中實現接口 IOPERATOR
| /***--- Sub.c ---******/ #include "lw_oopc_kc.h" #include"operater.h" #include "classes.h" /***********類 Sub 定義在 classes.h 中 CLASS(Sub) { IMPLEMENTS(IOPERATOR); };*/ static double GetResult(double a,double b) { return (a-b); } CTOR(Sub) FUNCTION_SETTING(IOPERATOR.GetResult,GetResult); END_CTOR /***----- END OF Sub.C-----****/ |
4、 組合,把 operater.h、Add.C、Sub.C 和 main.C 組成一個工程,main.c 文件如下:
| /***--- main.c ---******/ #include<stdio.h> #include "lw_oopc_kc.h" #include"operater.h" // 頭文件順序很講究,lw_oopc_kc.h 必須在前面 #include "classes.h" int main() { int a = 10, b=5; int c1,c2; IOPERATOR *poper; //--定義接口指針,用指針實現多態 Add A; //---對象 A 成員函數實現加法 Sub S; //---對象 B 成員函數實現減法 CLASS_CTOR(Add, A); CLASS_CTOR(Sub, S); //---靜態內存處理方法 poper = &A; //也可以動態內存方法:oper = New(Add); 記得 free() c1 = (poper->GetResult(a,b)); // c1 的結果 = 15 ( a+b) poper = &S; c2 = poper->GetResult(a,b); // c2 結果= 5 (a-b) return 0; } /***----- END OF main.C-----****/ |
總結:
1、在 lw_oopc_kc.h 的基礎上,為了增加可理解性,不改變原作含義為前提下,增加了以下宏
| #define CLASS_CTOR(Class,obj) Class##Setting(&obj) //--對象的構造宏 #define INHERIT(BASE) IMPLEMENTS(BASE) //---類繼承宏 #ifndef LW_OOPC_PURE_STATIC #ifndef LW_OOPC_STATIC #define New(Class) Class##New() //--對象動態構造宏 #endif #endif |
2、類的實例化必須有 3 步:
定義、構造、初始化。尤其初始化時候通常是通過指針的應用來實現對類內部成員的訪問。
3、繼承實現方法:
用父類名在子類中定義一個對象作為子類的一個成員變量,通過擁有該對象實 現子類對父類功能的擁有,即繼承。
4、注意:子類成員中用父類定義的對象,其構造函數要放在子類的初始化函數里(本人的解決方 法),因為子類的構造函數通過宏實現,不能直接調用父類的構造函數(如果您有更好辦法,請和給大家分享)。
5、 函數和函數指針的寫法:將函數名變為指針,函數參數只要參數說明。
e.g. double GetResult(double a,double b) 轉為函數指針為:
附:lw_oopc_kc.h 宏文件內容
| /* lw_oopc_kc.h */ #ifndef _LW_OOPC_H #define _LW_OOPC_H #include <stdlib.h> #define CLASS(type) \ typedef struct type type; \ struct type #ifndef LW_OOPC_PURE_STATIC #ifndef LW_OOPC_STATIC #ifndef LW_OOPC_DYNAMIC #define CTOR(type) \ void* type##Setting(type*); \ void* type##New()\ { \ struct type *t; \ t = (struct type *)malloc(sizeof(struct type)); \ return type##Setting(t); \ } \ void* type##Setting(type *t) \ { #else #define CTOR(type) \ void* type##New()\ { \ struct type *t; \ t = (struct type *)malloc(sizeof(struct type)); #endif #else #define CTOR(type) \ void* type##Setting(type *t) \ { #endif #endif #define END_CTOR return (void*)t; } #define FUNCTION_SETTING(f1, f2) t->f1 = f2; #define IMPLEMENTS(type) type type #define INTERFACE(type) \ typedef struct type type; \ struct type #endif /* end */ |
總結
以上是生活随笔為你收集整理的用 C 语言实现面向对象编程的全部內容,希望文章能夠幫你解決所遇到的問題。