马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
本帖最后由 SteDeshain 于 2015-2-2 22:46 编辑
看到论坛5周年庆,好热闹啊!简直就像狂欢一样!我个万年潜水党也是按捺不住了!
就写一些自己学到的东西,拿出来和大家交流。由于我是自学编程的,所以肯定有一些不好的地方!还请大家多多批评指正!
新人贴,还求别沉!
前段时间在学习时,需用到oo的一些技术,但鉴于当时我还不怎么会c++,我就在想,能不能用c就实现面向对象的那些功能呢?
网上一搜,果然有很多这样的实现,但可能是这种实现和工程本身的联系太大,找不到一套相对完整的代码
所以我就参照网上大神们发的一些代码,自己试着写了起来。
ps,下面大部分的思路都源于网络大神们,就不一一列名感谢了
首先,需要实现oo,当然至少要实现oo的三大特性:封装,继承,多态
1,封装+继承
我们可以用 c 中常用的原生的 struct 来实现
比如实现一个student的类,字段属性什么的就用结构中的字段了,至于类中的方法,自然想到 c 中的函数指针了!typedef struct _student
{
char *name;
int age;
bool sex;
void *Learn(void);
void *Play(void);
} student;
比如xiaoming是student的一个实例,那么调用对象xiaoming中的方法这样:xiaoming.Learn(); 就行了
但xiaoming.Learn终究只是个函数指针,它在struct _student中只占一个指针的大小,c的限制不能把整个函数都放进去,因为函数在c中是静态储存的,只储存一份!
那么这样问题来了
如果xiaohong是student的另一个实例呢?这样xiaohong.Learn();和xiaoming.Learn();做的事就一样了!完全体现不出来两个对象是不同的实例这一点了!
那么加一个参数就好了。在每一个类中的方法都加一个指定被调用对象的参数,为了统一,我们把这个参数放在所有参数中的第一个
以后所有类中的函数都要如此!这也是其他一些语言比如lua中常用的方法
关于这个参数具体怎样放到后面在讲
当然,上面的代码是非常简单的,但怎么做才能把它变成一可以new,可以delete,可以继承的类呢?
要new,要delete,就需我们实现构造函数和析构函数了。先不讲构造函数,先讲讲一个叫Init()的函数,它在后面有大用现在假设我们已经有了实现 继承 的方法了(即将会说到),那么就参考c#,所有的类都要继承自一个叫"object"的抽象类,我们只要在object中把Init()函数和析构函数作为虚函数,让它的子类去重写就好了!typedef struct _object
{
void *Init(void);
void *Delete(void);
} object;
重点来了,如何去继承呢?善用 c 的指针!在c中,有一个重要的特性,不同类型的指针之间可以肆意的转换!
既然如此,那么我们就把要用到的所有指针都看作 void * !
甚至是函数指针typedef void *OO_Func_Ptr;
typedef void *OO_Ptr;
这里我们加上了"OO_"的前缀,为防止命名冲突,就要在以后的oo实现有关名称前都要加这个前缀了
所以object类就变成了下面的模样typedef struct _OO_object
{
OO_Func_Ptr Init;
OO_Func_Ptr Delete;
} OO_object;
扯了这么多,还是没讲到怎么继承……说到继承,那就是子类里面要有其父类中的字段属性和方法,
想要student继承object,首先就会想到直接把整个object结构放进student里面,就像下面:typedef struct _OO_student
{
OO_object base;//父类
char *name;//成员
int age;
bool sex;
OO_Func_Ptr OO_Learn;//方法
OO_Func_Ptr OO_Play;
} OO_student;
这样student就有初始化函数Init()和析构函数Delete()了!
那么接下来,让我回到被放下的构造函数
构造函数无非就是在内存中开辟一块空间,作为一个新对象所存在的内存空间
我们用大家熟悉的malloc()函数OO_Ptr xiaoming = malloc(sizeof(OO_student));
再一次把构造函数暂时放下不谈……malloc之后是有了那么一块内存,但里面的数据都是无意义的随机数据
这时就需要我们一直在写的初始化函数Init()了
我们需要实现这一函数
前面说到,类中的每一个函数都要第一个参数指定特定的对象,那这个参数的类型就用到前面typedef的OO_Ptr了,因为前面说过要把要用到的所有指针都看作 void * ,也就是OO_Ptrvoid Init(OO_Ptr self)
{
(OO_student*)self->name = NULL;
(OO_student*)self->age = 0;
(OO_student*)self->sex = true;
}
看起来现在我们只需在malloc之后这样调用就ok了:typedef void (*OO_Object_Init_Type)(OO_Ptr);
OO_Ptr xiaoming = malloc(sizeof(OO_student));
((OO_Object_Init_Type)(((OO_object *)xiaoming)->Init))(xiaoming);
//因为我们在统一指针类型后,object里面的Init变成了OO_Func_Ptr,也就是void *,
//这里为了调用它,要先把它转换成原先的类型
//这里非常麻烦,但后面有一个解决办法
但在malloc之后,xiaoming结构内的数据都是无意义的,那么xiaoming->Init也就是一个野指针了!
但又不能在Init()中给xiaoming->Init赋值成我们刚才实现的Init()……
可见仅仅malloc是不够的,这就需要一个更加完善的构造函数了。
OO_Ptr OO_NewStudent()
{
OO_Ptr self = malloc(sizeof(OO_student));
(OO_object *)self->Init = (OO_Func_Ptr)Init; //把刚刚实现的Init()赋值
(OO_object *)self->Delete = (OO_Func_Ptr)Delete; //这是再别的地方实现的析构函数,主要是做free的工作的,这里就先略去了
(OO_student *)self->OO_Learn= (OO_Func_Ptr)Learn;
(OO_student *)self->OO_Play = (OO_Func_Ptr)Play; //当然Learn和Play需先实现,这里就略去了
return self;
}
值得注意的一点是,在object结构和student结构中声明那些方法时,他们的类型是OO_Func_Ptr,也就是void *,这里我们赋值的是至少带有一个参数的函数类型,但 c 就是允许这逆天的类型转换…
这时再像这样就没问题了typedef void (*OO_Object_Init_Type)(OO_Ptr);
OO_Ptr xiaoming = OO_NewStudent();
((OO_Object_Init_Type)(((OO_object *)xiaoming)->Init))(xiaoming);
之后在做一些typedef,改一些不必要的细节typedef void *OO_Func_Ptr;
typedef void *OO_Ptr;
typedef struct _OO_object
{
OO_Func_Ptr Init;
OO_Func_Ptr Delete;
} OO_object;
typedef OO_object *OO_object_ptr;
typedef void (*OO_Object_Init_Type)(OO_Ptr);
typedef struct _OO_student
{
OO_object base;
char *name;
int age;
bool sex;
OO_Func_Ptr Learn;
OO_Func_Ptr Play;
} OO_student;
typedef OO_student *OO_student_ptr;
OO_student_ptr OO_NewStudent()
{
OO_student_ptr self = (OO_student_ptr)malloc(sizeof(OO_student));
((OO_object_ptr)self)->Init = Init;
((OO_object_ptr)self)->Delete = Delete;
self->Learn = Learn;
self->Play = Play;
return self;
}
void Init(OO_Ptr self)
{
((OO_student_ptr)self)->name = NULL;
((OO_student_ptr)self)->age = 0;
((OO_student_ptr)self)->sex = true;
}
OO_student_ptr xiaoming = OO_NewStudent();
( (OO_Object_Init_Type) (((OO_object_ptr)xiaoming)->Init) )(xiaoming);
这样就做到一个简单的继承父类的类,并实例化它,并调用它的一个方法。虽说在调用方法时,非常的麻烦,但后面会有简化调用的方法,那时就要用到c另一个强大的武器——宏
但今天就先到这里吧
我的这个办法是借鉴了无数大神的思想,当然会有很多的毛病瑕疵
还请大家指出不好的地方
我也只是抛砖引玉,请大家多多交流!贡献出更简洁的方法让大家学习!!
|