一、類的封裝實作:借用高煥堂的宏頭檔案,類很容易封裝為如下的格式
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)
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、子類的定義:在類的開頭借用已經定義的類進行定義一個變量,為了更加明白,表明是繼承,增加一個宏定義:
#define INHERIT(BASE) IMPLEMENTS(BASE)
于是以類 B 繼承類 A 為例子如下:
CLASS(B)
INHERIT(A); // 繼承 A 類 int b; // 子類的成員 void (*init) (void*, int x);
void (*put) (void*);
2、子類的成員函數實作,為了友善辨識,類 B 的成員函數帶字尾 ‘_B’
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); //---輸出類成員值
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"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"operater.h"
/***********類 Sub 定義在 classes.h 中
CLASS(Sub)
};*/
return (a-b);
CTOR(Sub) FUNCTION_SETTING(IOPERATOR.GetResult,GetResult);
/***----- END OF Sub.C-----****/
4
4、 組合,把 operater.h、Add.C、Sub.C 和 main.C 組成一個工程,main.c 檔案如下:
/***--- main.c ---******/
#include<stdio.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)
/***----- 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() //--對象動态構造宏
2、類的執行個體化必須有 3 步:定義、構造、初始化。尤其初始化時候通常是通過指針的應用來實作對類内部成員的通路。
3、繼承實作方法:用父類名在子類中定義一個對象作為子類的一個成員變量,通過擁有該對象實 現子類對父類功能的擁有,即繼承。
注意:子類成員中用父類定義的對象,其構造函數要放在子類的初始化函數裡(本人的解決方 法),因為子類的構造函數通過宏實作,不能直接調用父類的構造函數(如果您有更好辦法,請和給大家分享)。
5、 函數和函數指針的寫法:将函數名變為指針,函數參數隻要參數說明。
e.g. double GetResult(double a,double b) 轉為函數指針為:
double (* GetResult)(double, double);
------------------------------------------------------------------------------------
附: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_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
t = (struct type *)malloc(sizeof(struct type));
#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
/* end */