面向对象的C语言编程-DynamicLinkageGenericFunctions--C语言中的偷梁换柱
文章目錄
- `DynamicLinkageGenericFunctions`
- Constructors and Destructors
- 方法、信息、類和對象
- `new`
- `delete`
- `clone`
- `differ`
- `sizeOf`
- `main`
- 總結(jié)
- `TIPS`
DynamicLinkageGenericFunctions
代碼倉庫
[代碼倉庫]:https://github.com/zzu-andrew/linux-sys/blob/6c19498a436902120eec7e08c18e4a74d04dafa2 b+j6/ooc/test/c.02/
簡介:如果你在公司維護的模塊底層又區(qū)分很多小模塊,在用戶選擇不同的模塊時其他的模塊根本不需要運行,那么這個方法很適合你,通過將方法都做成靜態(tài)方法,可以有不同的對象,但是這些對象都通過函數(shù)指針調(diào)用處理自己數(shù)據(jù)的函數(shù)。
用處:
1. 面向?qū)ο?#xff0c;編程風格簡潔
2. 可以完美解決程序中到處都是的if-lse
Constructors and Destructors
構(gòu)造與析構(gòu)函數(shù)
加入我們像下面這樣使用函數(shù),為每個類型都定義自己的析構(gòu)函數(shù),那么就有一個問題,需要為所有的數(shù)據(jù)安裝析構(gòu)函數(shù)和編寫new函數(shù),如下:
struct type {size_t size; /* size of an object */void (* dtor) (void *); /* destructor */ }; struct String {char * text; /* dynamic string */const void * destroy; /* locate destructor */ }; struct Set {... information ...const void * destroy; /* locate destructor */ };new數(shù)據(jù)初始化只是new函數(shù)的部分工作,需要將dtor函數(shù)指向新創(chuàng)建對象中的析構(gòu)函數(shù),并且new中傳入不同的參數(shù)時需要實現(xiàn)不同的功能。
?
new(Set); /* make a set */ new(String, "text"); /* make a string */需要進行初始化時,new將會根據(jù)不同的對象調(diào)用不同的構(gòu)造函數(shù)對對象進行初始化,這里需要將對象中的函數(shù)指針ctor執(zhí)行對應的構(gòu)造函數(shù),指向的構(gòu)造函數(shù)如下,用于初始化除了對象之外的對象成員。
/* 構(gòu)造函數(shù) */ void * new (const void * _class, ...) { const struct Class * class = _class;void * p = calloc(1, class -> size);assert(p);/* 強制轉(zhuǎn)換為Class,這樣就能實現(xiàn)同樣的函數(shù)只賦值初始化一次,但是所有定義的對象都能夠調(diào)用 */* (const struct Class **) p = class;if (class -> ctor){ va_list ap;va_start(ap, _class);p = class -> ctor(p, & ap);va_end(ap);}return p; } /* 析構(gòu)函數(shù) */ static void * String_ctor (void * _self, va_list * app) { struct String * self = _self;const char * text = va_arg(* app, const char *);self -> text = malloc(strlen(text) + 1);assert(self -> text);strcpy(self -> text, text);return self; }從上述構(gòu)造函數(shù)的實現(xiàn)可以看出,這個構(gòu)造函數(shù)是沒有負責對象的構(gòu)建的,只是對對象中的成員進行了構(gòu)造(初始化),因為對象的構(gòu)造和析構(gòu)是new和delete函數(shù)負責的。
并且delete函數(shù)也是只負責釋放釋放new函數(shù)申請的內(nèi)存,所有構(gòu)造函數(shù)申請的內(nèi)存全部由析構(gòu)函數(shù)負責進行釋放。
/* delete函數(shù)只負責釋放new函數(shù)申請的內(nèi)存 */ void delete (void * self) { const struct Class ** cp = self;if (self && * cp && (* cp) -> dtor)self = (* cp) -> dtor(self);free(self); } /* 所有構(gòu)造函數(shù)中申請的內(nèi)存由,析構(gòu)函數(shù)這里負責釋放 */ static void * String_dtor (void * _self) { struct String * self = _self;free(self -> text), self -> text = 0;return self; }對于一個String,有一部分內(nèi)存是通過構(gòu)造函數(shù)進行申請的,但是String卻是new函數(shù)申請的內(nèi)存,String本身需要使用delete函數(shù)進行釋放。
方法、信息、類和對象
delete函數(shù)中傳入的對象指針,必須在傳入前初始化好,將析構(gòu)函數(shù)指針指向?qū)獙ο蟮奈鰳?gòu)函數(shù)
struct Class {size_t size;void * (* ctor) (void * self, va_list * app);void * (* dtor) (void * self);void * (* clone) (const void * self);int (* differ) (const void * self, const void * b); };struct String {const void * class; /* must be first */char * text; };每個對象的開頭都有一個指向自類型的指針const void * class,通過這個指針我們能獲取得到new對象時需要申請的內(nèi)存大小 .size,構(gòu)造函數(shù).ctor,析構(gòu)函數(shù).dtor,.clone函數(shù)對象復制函數(shù),.differ函數(shù)對象對比函數(shù)。
仔細看來我們的每個對象開頭都有一個指向自己類型的指針const void * class;,通過calss的類型中的提供的信息我們能知道如下信息:
size_t size; // 提供對象的大小,這里也就是String的大小 void * (* ctor) (void * self, va_list * app); // 構(gòu)造函數(shù) void * (* dtor) (void * self); // 析構(gòu)函數(shù) void * (* clone) (const void * self); // 對象克隆函數(shù) int (* differ) (const void * self, const void * b); //對象對比函數(shù)new
new函數(shù)中申請了對象的內(nèi)存,并通過構(gòu)造函數(shù)對對象中的內(nèi)容進行了構(gòu)造,其實這里也就是一個text字符串指針,申請一塊內(nèi)存,并將new中傳入的字符串復制到申請的內(nèi)存中去。
需要注意的是二級指針的使用方法,通過new中申請的是struct String那么p是指向String類型的一個對象,但是Stirng類型中class才是指向靜態(tài)方法的結(jié)構(gòu)體的指針,想要調(diào)用方法,就需要取出p指針中的class指針。實現(xiàn)方法就是* (const struct Class **) p = class;,要求就是,class必須是object中的首個指針。
拆分:
* (const struct Class **) p在按照結(jié)合方式從左至右 *先與p結(jié)合,然后是(const struct Class **) 上述等價于:p->class ==> (*p).class* (const struct Class **) p ==> (const struct Class *)p->class void * new (const void * _class, ...) { const struct Class * class = _class;void * p = calloc(1, class -> size);assert(p);/* 強制轉(zhuǎn)換為Class,這樣就能實現(xiàn)同樣的函數(shù)只賦值初始化一次,但是所有定義的對象都能夠調(diào)用 */* (const struct Class **) p = class;if (class -> ctor){ va_list ap;//ctor 指向的函數(shù) String_ctorva_start(ap, _class);p = class -> ctor(p, & ap);va_end(ap);}return p; } static void * String_ctor (void * _self, va_list * app) { struct String * self = _self;const char * text = va_arg(* app, const char *);self -> text = malloc(strlen(text) + 1);assert(self -> text);strcpy(self -> text, text);return self; }delete
delete函數(shù)是對new申請的內(nèi)存進行釋放的函數(shù),在確保傳入的值不是NULL并且構(gòu)造函數(shù)存在的情況下先調(diào)用析構(gòu)函數(shù)對self中構(gòu)造函數(shù)申請的內(nèi)存進行釋放,在調(diào)用free釋放new函數(shù)申請的內(nèi)存。
void delete (void * self) { const struct Class ** cp = self;if (self && * cp && (* cp) -> dtor)self = (* cp) -> dtor(self);free(self); }析構(gòu)函數(shù)
static void * String_dtor (void * _self) { struct String * self = _self;free(self -> text), self -> text = 0;return self; }clone
對象的克隆
void * clone (const void * self) { const struct Class * const * cp = self;assert(self && * cp && (* cp) -> clone);return (* cp) -> clone(self); }對象的克隆,就是調(diào)用new方法新生成一個和傳入對象一樣的對象
static void * String_clone (const void * _self) { const struct String * self = _self;return new(String, self -> text); }differ
差異對比函數(shù),實現(xiàn)了對對象本身以及對象字符串是否相同的對比,如果對象相等那就返回0,如果是不同的對象,對象描述text一樣也放回0,表示相同;
int differ (const void * self, const void * b) { const struct Class * const * cp = self;assert(self && * cp && (* cp) -> differ);return (* cp) -> differ(self, b); } static int String_differ (const void * _self, const void * _b) { const struct String * self = _self;const struct String * b = _b;if (self == b)return 0;if (! b || b -> class != String)return 1;return strcmp(self -> text, b -> text); }sizeOf
sizeOf函數(shù)比較簡單,內(nèi)部只是返回對象中size字段中存儲的數(shù)值
size_t sizeOf (const void * self) { const struct Class * const * cp = self;assert(self && * cp);return (* cp) -> size; }main
#include <stdio.h> #include "String.h" #include "new.h"int main () { void * a = new(String, "a"), * aa = clone(a);void * b = new(String, "b");printf("sizeOf(a) == %zu\n", sizeOf(a));if (differ(a, b))puts("ok");if (differ(a, aa))puts("differ?");if (a == aa)puts("clone?");delete(a), delete(aa), delete(b);return 0; }總結(jié)
這小節(jié)想說的其實就是動態(tài)的指針,同樣的應用程序?qū)⒑瘮?shù)指針指向不同的函數(shù),就能實現(xiàn)不同的效果.
在主函數(shù)中創(chuàng)建了a、aa、b對象,三個對象都通過calss指針指向了共同的Class結(jié)構(gòu)體, Class結(jié)構(gòu)中有指向靜態(tài)函數(shù)的指針,在編譯的時候通過編譯不同的c文件實現(xiàn)對函數(shù)指針裝載不同的函數(shù)。
實現(xiàn)的效果也就是通過動態(tài)指針指向不同的函數(shù),從而實現(xiàn)同樣的主函數(shù),實際執(zhí)行的時候,執(zhí)行不同的函數(shù),達到不同的效果,實現(xiàn)對數(shù)據(jù)的封裝。
TIPS
#include <stdio.h> #include <stdlib.h> #include <assert.h> #include <string.h> #include <stdarg.h>void test(void);struct String {const void * class; /* must be first */char * text; };struct Class {size_t size;void (* test) (void); }; void test(void) {printf("test for point.\n"); }static const struct Class _String = {sizeof(struct String),test };const void * String = & _String;/*** */ int main(int argc, char const *argv[]) {size_t stringSize = sizeof(struct String);const struct Class * class = String;void * p = calloc(1, class -> size);assert(p);* (const struct Class **) p = class;struct String {const void * class; /* must be first */char * text;};struct String * p = (struct String *)calloc(1, sizeof(struct String));return 0; }我們有如下定義:
struct String {const void * class; /* must be first */char * text; }; struct Class {size_t size;void (* test) (void); };首先我們申請一個內(nèi)存void * p = calloc(1, sizeof(struct String));,這里很好理解,這個時候我們想讓p變成一個指向struct String類型的一塊內(nèi)存,只需要申請的時候加上強制轉(zhuǎn)換就可以了struct String * p = (struct String *)calloc(1, sizeof(struct String));,這個時候p就是一個指向struct String類型元素的一個結(jié)構(gòu)體指針。
接下來看下這個使用方法*(const struct Class **) p = class;很多人看到第一眼的時候,第一眼就本能的將后面兩個取址符與前面一個解引用相互抵消最終得出下面這樣的結(jié)論,實際效果相當于:
(const struct Class *) p = class;,你要是真的這樣想就大錯特錯了。
這里的意思其實是如下圖:
在經(jīng)過上述操作之后,p指針指向了String結(jié)構(gòu)體,而* (const struct Class **) p = class;的作用就是講String中的void *類型的指針class指向String結(jié)構(gòu)體,注意是結(jié)構(gòu)體不是類型,解如圖中那樣,String結(jié)構(gòu)體其實是一個struct Class類型的結(jié)構(gòu)體指針。
static const struct Class _String = {sizeof(struct String),test };const void * String = & _String; 所以下面這個絕對不能使用取址和解引用相互抵消的方式進行代碼走讀,也不要新百度問答上那些講的這不這樣使用不能達到能將String結(jié)構(gòu)體中的void *指針指向新申請內(nèi)存的目的const struct Class ** -- 告訴編譯器這個指針是個二維指針,第一個指針是指向String結(jié)構(gòu)體,結(jié)構(gòu)體中又有兩個指針,第二個指針說明的是取結(jié)構(gòu)體指針中的指針,具體取得哪個指針就由前面給出的類型來決定了,這里取得是一個`struct Class`結(jié)構(gòu)體指針。因為這個時候取得是雙重指針,有因為p本身又是指針,要想和class這個指針對應上,需要對指針進行一次解引用,這也就形成了下面這種方式,在C語言中實現(xiàn)取結(jié)構(gòu)體指針中的指針的方式 * (const struct Class **) p = class;總結(jié)
以上是生活随笔為你收集整理的面向对象的C语言编程-DynamicLinkageGenericFunctions--C语言中的偷梁换柱的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2016中国国际大数据大会邀请函
- 下一篇: 多中心临床大数据平台建设及深度应用