天天看點

用 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)

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     */