由于长期跟面向对象开发语言打交道,作为一个c语言入门者(差不多零基础),接触C语言最大疼点就是不习惯面向过程的开发。为了快速上手c语言,还是使用自己习惯编程思维去开发。这次主要针对面向对象封装、继承、多态3大特点进行实现。以下记录一下花了一整晚写的c语言面向对象的代码。
一、首先创建头文件object.h,该头文件是主要作用是定义面向对象一些关键字,代码如下
#include <stdlib.h>
#include <string.h>
#include "base.h"
#ifndef OBJECT_H
#define OBJECT_H
//
#define PUBLIC ;
#define PRIVATE(PrivateName) struct PrivateName
#define CLASS(ClassName) struct ClassName
#define THIS(ClassName) CLASS(ClassName)*this
#define PLACEHOLDER(ClassName) char placeholder[sizeof(CLASS(ClassName))-sizeof(char*)] //substract first pointer size
//memory define
#define NEW(type) ((type*)malloc(sizeof(type)))
#define DELETE(memory) free(memory)
static void setValue(void* property, void* value,U32 len)
{
memcpy(property, value,len);
}
static void getValue(void* property, void* value,U32 len)
{
memcpy(value, property,len);
}
#endif
上述代码中PUBLIC、PRIVATE、CLASS、THIS、NEW、DELETE为面向对象语言最常见的关键字,定义的目的为了简化书写及方便理解,并实现本次面向对象的功能。以下介绍一下各个关键字作用:
PUBLIC:定义公共的变量及函数,使外部能进行访问
PRIVATE:通过在private.h头文件中定义不开放变量及函数,达到对象封装的效果
CLASS:定义一个结构体,用作类信息定义
THIS:将CLASS的第一个成员this指向自己,目的为了在继承的时候,通过this找到基类的成员。
NEW:为类分配空间,实例化对象
DELETE:删除分配的空间,跟NEW成对出现
setValue,getValue为数据的get、set函数,用于对象数据获取与设置
注:PLACEHOLDER是后来加上去的,博主在使用过程中发现子类不能定义变量和函数指针,原因是子类的私有变量使用了THIS后面的地址空间,结果直接覆盖了父类的内存,所以增加PLACEHOLDER做占位用,使子类定义的变量和函数指针能指向正确的地址
二、创建基类person.h,根据面向对象类定义的实现封装效果
#include <stdlib.h>
#include <string.h>
#include "../../../base/include/object.h"
#include "person.h"
#ifndef MAN_H
#define MAN_H
CLASS(Man);
PUBLIC void Man_constructor(CLASS(Man)*);
PUBLIC void setname_man(CLASS(Person)* self,char* str);
PUBLIC void getname_man(CLASS(Person)* self,char* str);
CLASS(Man){
THIS(Person);
PLACEHOLDER(Person);//fix memory overwrite bug
};
void testMan();
#endif
这里代码有点多,我简单说明一下思法:
person_private.h主要是私有类型,里面定义的内容不开放给外部,内容主要如下:
#include <string.h>
#include "../../../base/include/object.h"
#ifndef PERSON_PRIVATE_H
#define PERSON_PRIVATE_H
PRIVATE(Person_Private);
PRIVATE(Person_Private)
{
int age;
char name[255];
};
#endif
CLASS(Person),这里定义一个类信息,其中THIS为当前类实例的指针
Person_constructor为构造函数,主要初始化CLASS(Person)对象信息,如绑定setage、getage、setname、getname各种函数
三、用上面person封装思想来创建子类man.h,并实现面向对象继承及多态的特性
#include <stdlib.h>
#include <string.h>
#include "../../../base/include/object.h"
#include "person.h"
#ifndef MAN_H
#define MAN_H
CLASS(Man);
PUBLIC void Man_constructor(CLASS(Man)*);
PUBLIC void setname_man(CLASS(Person)* self,char* str);
PUBLIC void getname_man(CLASS(Person)* self,char* str);
CLASS(Man){
THIS(Person);
};
PUBLIC void Man_constructor(CLASS(Man)*self)
{
self->this=(CLASS(Person)*)self;
Person_constructor(self->this);
self->this->getName=getname_man;
self->this->setName=setname_man;
}
PUBLIC void setname_man(CLASS(Person)* self,char* str)
{
str = strcat(str, " man set");
setValue(self->private.name,str, strlen(str)+1);
}
PUBLIC void getname_man(CLASS(Person)* self,char* str)
{
getValue(self->private.name, str, strlen(self->private.name)+1);
strcat(str, " man get");
}
void testMan();
#endif
由上面代码可以看出,子类的代码比父类少了很多,这就是面向对象的好处了

THIS(Person)作用是将子类地址入口指向CLASS(Man)的第一个成员,用这个做法可以通过CLASS(Person)*强制转换CLASS(Man)对象。
Person_constructor(self->this)作用是子类被实例化构造时,对基类也进行实例化。这里就是继承了,是不是很简单呢。
self->this->getName=getname_man和self->this->setName=setname_man就是面向对象多态的实现,其实就是覆盖父类的方法(高级语言叫重写)
四、实现man.c和oo.c,看看面向对象实现的效果吧
man.c代码如下
#include <stdio.h>
#include "../include/man.h"
void testMan()
{
CLASS(Man)* man = NEW(CLASS(Man));//new
Man_constructor(man);//构造函数
CLASS(Person)* person=(CLASS(Person)*)man;
char str[255]="person";
person->setName(person,(char*)str);
char name[255];
person->getName(person,name);
person->setAge(person,20);
int age = person->getAge(person);
DELETE(man);
printf("%s",name);
printf("%d",age);
fflush(stdout);
}
oo.c代码如下
#include <stdio.h>
int main(int argc,char* argv[])
{
testMan();
return 0;
}
下面是调用执行的结果,可以看出这里成功打印子类的信息及继承父类的信息,证明这次面向对象实现是成功的
另外需要补充说明一下,为了方便文中类定义都是头文件中完成,实际项目应放在具体的c文件里面,原因是避免编译时出现重复定义的问题