鱼C论坛

 找回密码
 立即注册
查看: 1490|回复: 0

[技术交流] 【小白吹水】关于用pure C实现一些面向对象功能的一点拙见

[复制链接]
发表于 2015-2-2 22:48:30 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

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_Ptr
void 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另一个强大的武器——
但今天就先到这里吧

我的这个办法是借鉴了无数大神的思想,当然会有很多的毛病瑕疵
还请大家指出不好的地方
我也只是抛砖引玉,请大家多多交流!贡献出更简洁的方法让大家学习!!


想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-11-25 16:03

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表