doubango ANSI-C 对象编程的实现方法

来源:互联网 发布:野性美的意思知乎 编辑:程序博客网 时间:2024/05/29 15:25


doubango 3GPP IMS/LTE Framework v1.0.0

 

Programmer’s Guide


正如你知道的,C语言不是一个面向对象语言今天,OOP面向对象编程)是编写精心设计软件的最好方式

在这份文档中“被定义的对象”是指一个特殊C语言结构体本文的所有功能是tinySAK项目一部分。为了解释如何定义对象及如何使用对象我会使用一个基于对象例子。“人”对象被声明是这样的:

typedef struct person_s{TSK_DECLARE_OBJECT; /* Mandatory */char* name;struct person_s* girlfriend;}person_t;

对象定义可以被视为一个类的定义。该定义包括对象的必须具备(强制性)的功能,对象的大小和一个引用计数器。强制性的功能包括:构造函数,析构函数和比较函数C结构体通过在其体内TSK_DECLARE_OBJECT宏#define为对象。

对象定义指针指向一个结构体 tsk_object_def_s

typedef struct tsk_object_def_s{//! The size of the object.size_t size;//! Pointer to the constructor.tsk_object_t*(* constructor) (tsk_object_t *, va_list *);//! Pointer to the destructor.tsk_object_t*(* destructor) (tsk_object_t *);//! Pointer to the comparator.int(*comparator) (const tsk_object_t *, const tsk_object_t *);}tsk_object_def_t;

创建一个对象分为两个阶段。第一阶段是动态在堆上分配对象; 这就是为什么对象定义结构内它的大小强制性的原因一个新的对象是在堆上分配,其所有成员(char*,void*,int,long...被清零在第二阶段将新创建的对象通过调用提供的构造函数初始化。为了执行这两个阶段,你应该调用tsk_object_newtsk_object_new_2()


对象被销毁分两个阶段进行。第一阶段释放其成员(void*,char* ...函数负责这项任务的第二阶段对象本身被销毁。因为对象不能销毁自己使用tsk_object_unreftsk_object_delete执行这两个阶段接下来部分对这两个函数之间差异进行了解释

下面是如何声明一个对象定义的例子:

//(Object defnition) static const tsk_object_def_t person_def_t =  { sizeof(person_t), person_ctor, person_dtor, person_cmp}
构造函数是负责初始化,不会分配对象传递给构造方法对象已经被分配

下面是一个例子

// (constructor)static tsk_object_t* person_ctor(tsk_object_t * self, va_list * app){ person_t *person = self; if(person){ person->name = tsk_strdup(va_arg(*app, const char *)); } return self; }

析构函数会释放对象的成员不会破坏对象本身(第一阶段)析构函数必须返回一个指针允许调用者执行第二阶段

// (destructor) static tsk_object_t * person_dtor(tsk_object_t * self) {  person_t *person = self; if(person){ TSK_FREE(person->name);tsk_object_unref(person->girlfriend); } return self; }

比较函数是用来比较两个定义的对象比较对象应具有相同的定义或类型

// (comparator)static int person_cmp(const tsk_object_t *_p1, const tsk_object_t *_p2) { const person_t *p1 = _p1; const person_t *p1 = _p2;int ret;// do they have the same name?if((ret = tsk_stricmp(p1->name, p2->name))){return ret;}// do they have the same girlfriend?if((ret = tsk_object_cmp(p1->girlfriend, p2->girlfriend))){return ret;}// they are the samereturn 0; }

引用计数用来模拟垃圾收集每个被定义对象包含一个引用计数器字段这表明多少对象对该对象进行引用

一个对象被创建(见下文)计数器的值被初始化为1这是自动完成你不用做任何事情计数器加1当你调用tsk_object_ref递减1调用tsk_object_unref计数器的值达到,然后对象被回收(释放

正如你认为的那样,ANSI-C是不支持继承的。因为任何C结构可以强制转换为它的第一个元素指针继承可以这样实现

#include "tsk.h"// (a student is a person)typedef struct student_s{struct person_s* person; // Must be the first elementchar* school;}student_t;// (as a student is a person you can do)student_t* s;((person_t*)s)->name = tsk_strdup("bob");
因为person_t是个定义的对象,那么student_t也是。
一旦声明了对象的定义以及实现了对象所有强制性功能,它可以这样被使用:

// creates a person: will call the constructorperson_t* bob = tsk_object_new(&person_def_t, "bob");// creates bob's girlfriendbob->girlfriend = tsk_object_new(&person_def_t, "alice");// deletes bob: will delete both bob and bob's girlfriend field by calling their destructorstsk_object_unref(bob);
由于很难记住构造函数需要哪个参数(As it’s hard to guest which parameters the construct expects),一般我们用宏或函数来帮助。宏定义是这样的:

// create a person#define PERSON_CREATE(name)tsk_object_new(&person_def_t, (const char*)name)
由于函数有固定参数有一个共同的宏来销毁所有定义的对象 TSK_OBJECT_SAFE_FREE()可用来销毁任何对象只有对象引用计数减1并等于零时,对象将被释放在所有情况释放或不释放的指针值设置为NULL。
上面的例子可以写成这样

#include "tsk.h"// create a person: will call the constructorperson_t* bob = PERSON_CREATE("bob");// create bob's girlfriendbob->girlfriend = PERSON_CREATE("alice");// delete bob: will delete both bob and bob's girlfriend field by calling their destructorsTSK_OBJECT_SAFE_FREE(bob);
原创粉丝点击