c语言结构体模拟c++对象的方法,并实现this指针

来源:互联网 发布:阿里云备案拍照地点 编辑:程序博客网 时间:2024/05/16 17:32

先说一下结构体模拟对象的基本思路。
1. 在结构体里定义好函数指针。
2. 在结构体外面定义好对象的方法。
3. 在结构体初始化的时候把这些方法的地址赋值给对应的函数指针。
4. 通过函数指针调用函数,并把结构体自身的地址传给函数。

这个思路容易想到,但不是很完美。毕竟定义函数的时候必须多写一个参数指向结构体(即this指针必须通过参数显式传递),不方便。调用的时候也要多写一次对象的名字:obj.method(&obj,arg1,arg2,…)。

于是就有了一个改进的思路:this指针不通过参数传递,而是调用方法的时候临时放到一个全局变量里,到方法里面再取出来。并且可以借助宏来尽可能封装,简化这一过程。

先来看一下这个头文件:

/*jscobj.h*/#ifndef JSCOBJ_H#define JSCOBJ_H#define this m(self)#define m(pInst) ((pInst)->_method_(pInst))#define Define_Method(T) T * T##$Method(T *ps){_obj_arg_ = ps;return ps;}#define Define_Member(T) struct T * (* _method_)(struct T * ps);#define Declare_Method(T) T * T##$Method(T * ps);#define Register_Method(pInst,T) pInst->_method_ = T##$Method;#define MethodOf(T) T * self = (T *)_obj_arg_;extern void *_obj_arg_;#endif
/*jscobj.c*/#include "jscobj.h"void *_obj_arg_;

这里定义了七个宏一个全局变量。下面先来看具体的用法。
这里定义了一个Student类做例子

/*student.h*/#ifndef _STUDENT_H_#define _STUDENT_H_#include<stdio.h>#include<stdlib.h>#include"jscobj.h"typedef struct Student{    /****/Define_Member(Student)/****/    void (*printInfo)();    int (*getAge)();    int age;    char name[10];}Student,*pStudent;/****/Declare_Method(Student)/****/int Student$getAge();void Student$printInfo();pStudent Student$$new(char *name,int age);void Student$$Ini(pStudent stu, char *name, int age);#endif
/*student.c*/#include "student.h"#include "string.h"/****/Define_Method(Student)/****/pStudent Student$$new(char *name, int age){    pStudent stu = (pStudent)malloc(sizeof(Student));    Student$$Ini(stu,name,age);    return stu;}void Student$$Ini(pStudent stu,char *name, int age){    /****/Register_Method(stu,Student)/****/    stu->getAge = Student$getAge;    stu->printInfo = Student$printInfo;    stu->age = age;    strcpy(stu->name,name);}int Student$getAge(){    /****/MethodOf(Student);/****/    return self->age;}void Student$printInfo(){    /****/MethodOf(Student);/****/    printf("name: %s, age: %d\n",this->name,this->getAge());}

如上,用到宏的地方着重标了一下。这些宏看起来就和注释差不多,但是实际上做了什么呢?

首先,Define_Member实际上在结构体里面定义了一个函数指针 _member_ ,然后Declare_Method 和 Define_Method 定义了一个函数,这个函数的作用就是把对象地址放到全局变量里面去。最后通过Register_Method把这个函数注册到_member_ 指针上,这样等于结构体多了一个隐藏的方法。

然后,来看下实际是怎么样调用方法的:

/*main.c*/#include<stdio.h>#include<stdlib.h>#include"student.h"int main(){    Student *stu = Student$$new("Jack",20);    m(stu)->printInfo();    system("pause");    return 0;}
/*输出:*/name: Jack, age: 20

m(stu)->printInfo() 这里,展开m这个宏以后变成 ((stu)-> _method_(stu))->printInfo(); 也就是先用_method_把stu的地址放到全局变量以后再调用方法。接着在方法里面,第一行MethodOf(Student)把这个地址取出,并转化成一个Student指针,名字是self。这样,被调用的方法就知道是哪个对象调用它了。

最后,在方法里面,因为知道对象自己的地址就储存在self里面,那么通过self调用其他方法的时候就没必要再m(self),而可以通过另一个词this来替换掉m(self)。这样在方法内部,可以直接做到像this->getAge()这样的调用。这样this指针就通过宏实现了。

1 0