使用C语言的struct来实现C++的class

如何在C语言中实现C++的Class呢?
一些低级设备好比嵌入式,或者一些底层驱动、操做系统中,是不能使用C++语言的。网上有不少对这方面的解释,除非对编译器作一些设置或者修改,但这样大大增长了开发的难度。而且靠修改编译器参数来编译,仍旧不能利用到C++的优势。好比C++的虚函数,垃圾回收,异常,在底层开发中使用,反而会形成不少没必要要的麻烦。好比C++编译器为了重载函数,其编译出来的函数名会被改为包括参数的形式,并且每一个编译器都有本身的内部前缀和后缀,这一点尤为在操做系统编写中会形成麻烦,由于操做系统的系统调用使用汇编,好比中断的实现中,就须要调用汇编中断服务,而后在其中回调操做系统内核的C函数,若是使用C++函数,就不能正确指定回调函数名。那么在只能使用C语言来实现的状况下,如何让这种过程式语言更具封装性,而不让代码看起来“懒散”呢?
C语言中能够和class类比的类型就是struct了,另外还有union, 但union并不具有class的条件。在struct中不能定义函数, 这一点能够在Microsoft Visual Studio中和Linux GCC下作个比较:
typedef struct A {
int data;
int Val() {
 return data;
}
}A;
A a;
a.Val();
在VS下这个struct能经过编译,而且a.Val()能取到值, 这是由于C++编译器在对兼容C语言的struct进行编译时,是将struct按照public class来理解的,因此能支持内联函数。但GCC是只支持C语言的编译器,编译时就会报错。那么,若是使用C语言,如何才能让struct媲美class呢?
其实C类语言都支持函数指针的定义,而且struct中也支持函数指针定义。好比
int func(int a, int b);
定义的函数指针可使这样的:
int (*pfunc)(int, int);
当定义pfunc = func时,下面两个调用是同样的:
func(10, 20);和pfunc(10, 20);
那如上面所说,将函数指针定义到struct中:
typedef struct A {
int data;
int (*Val)(int a);
}A;
int Val(int a) {
 return a;
}
A a;
a.Val = Val;
a.Val(10);
这样能够获得10的结果。咱们知道class中隐含了一个this指针,那在Val函数中怎样才能获得this呢?对了,就经过参数传递进去:
typedef struct A  A;
struct A{
int data;
int (*Val)(A* that, int a);
};
int Val(A* that, int a) {
 return that->data + a;
}
A a;
a.Val = Val;
a.Val(&a, 10);
上面就能够获得a.data + 10的结果。咱们使用that来代替this,这样若是这段代码拿到C++编译器下面时也不会跟struct中隐含的this冲突。这样就定义了struct来代替class,惟一的缺点是定义好之后,每次调用函数须要将对象指针传递进去,这也是无可避免的。能够定义下面的宏来防止两次指定的对象不一致:
#define __CALL(o, f, ...) o.f(&o, __VA_ARGS__)
__CALL(a, Val, 10);
其中__VA_ARGS__是C语言关键字,用于将宏的变参传递到指定位置。在编译期宏就已经被展开,所以Val已是a的成员(函数)了,因此不用担忧Val这个参数在__CALL这个宏调用时没有定义。
进阶1: 构造函数
上一步中,a.Val = Val;写在外面,若是有好几个成员(函数),会形成代码臃肿,须要定义一个构造函数。
A * _A(A* that, int data) {
that->data = data;
that->Val = Val;
return that;
}
A a;
_A(&a, 20);
a.Val(&a, 10);
这样定义一个对象就须要两行代码,仍旧能够定义宏来实现新建对象,不过若是是new对象彻底没有必要。
A * a = _A(new A, 10);
另外这里构造函数只能是一个普通函数,不能做为成员(函数),由于构造函数只调用一次,没有做为成员的必要,而且构造函数若是是成员也无法在构造前知道构造函数是什么,所以只能在外部指定。若是非要定义成成员(函数),这就变成了两行代码:
a.init = _A;
a.init(&a, 20);
哈哈,可是若是想要从新设置对象a的值, 定义init成员则另当别论,不过最好仍是在普通函数_A中定义。
进阶2:继承
咱们已经有了一个很好的"class"了:
typedef struct A  A;
struct A{
int data;
int (*Val)(A* that, int a);
};
int Val(A* that, int a) {
 return that->data + a;
}
A * _A(A * that, int data) {
that->data = data;
that->Val = Val;
return that;
}
A a;
_A(&a, 20);
a.Val(&a, 10);
接下来,咱们要实现继承。由于若是只是须要上面的代码,就没有使用类的必要,实现继承才是使用类的终极目标。这里先暂时、并且也无法实现虚函数之类的,不用考虑这些。实现继承须要用到上面提到的union,好比继承上面的A:
typedef struct B B;
struct B {
union {
A super;
struct {
int data;
int (*Val)(A* that, int a);
};
};
int val;
};
B* _B(B* that, int val) {
_A(&that->super val);
that->val = val;
}
B b;
_B(&b, 30);
在union中,定义了基类A,以及将基类A中的成员都拷贝到了一个匿名struct中。在C规范中,union的匿名struct后面定义一个变量名那么使用变量名.成员才能获得变量,而若是没有变量名,则直接使用成员名就能获得变量,以下:
union {
float f[2];
struct {
float f1;
float f2;
}uf;
}um;
要获得f[1]使用um.fu.f2能够获得,而
union {
float f[2];
struct {
float f1;
float f2;
};
}um;
只使用um.f2就能获得f[1]。咱们的类就是利用了这点,可让基类的成员变成继承类的成员。继承类中super部分是基类,而自身又定义了val这个成员变量,是属于基类之外的,并且更有意思的是,在B的构造函数中,能够直接经过that->super来构造a,而且构造函数完了之后,b.data和b.Val就是构造A之后的成员,它们分别等于b.super.data和b.super.Val。
进阶3:部分继承和重写(重载)
另外咱们还可使用这种结构来实现部分继承。来看下面A和B的定义(只有定义,前向声明和调用省略):
struct A{
int data;
int (*Val)(A* that, int a);
int val;
};
struct B {
union {
A super;
struct {
int data;
int (*Val)(A* that, int a);
};
};
int val;
};
其中,B.val和A.val是不一样的成员。若是要取得A.val使用b.super.val就能够了, 这个是union的特性来决定的。这种没有继承基类的状况叫部分继承。
那么怎么实现重写呢?来看B的构造函数:
B* _B(B* that, int val) {
_A(&that->super, val);
//override
that->Val = myVal;
that->val = val;
}
只要将继承类的Val指针指向自定义的函数就能够了。不过注意必须在A构造完成以后,不然会被覆盖回来。
因此概括起来,构造函数的写法顺序为:
基类构造
重写函数
子类构造(函数指针初始化)
子类数据初始化
其余初始化
其中,“其余初始化”以前的全部均可以类比为C++类的构造函数:
B : A(val), val(val) {}
而重载函数是在C++类中定义内联函数时就已经直接重载了。
进阶4:定义宏使结构更简单
C语言模拟C++类的大致如上。不过若是想要建立类更简便,还须要一些宏定义的帮助。
在任何状况下,咱们都应该知道本身定义这个类未来是否是会成为基类。因此,对于A,咱们知道它是基类,能够将它改写成“模板”,但这个模板非C++的template,只是宏定义,用于简化继承类中基类的书写:
typedef struct A {
#define A_Template \
int data;\
int (*Val)(A* that, int a);
#define Template_A A_Template
Template_A
int val;
}A;
虽然多了两个#define,可是A的定义并无变化。int val;不是由子类继承的因此不用写在#define里面。#define包括在花括号内是为了让代码更加美观。#define将会在下面宏中使用:
#define __SUPER(Base) \
union {\
 Base super; \
 struct {\
  Template_##Base\
  }; \
 }
这就是B的union部分,咱们将它提炼出来,使得之后全部的类均可以不失通常性地调用宏来继承。
typedef struct B {
__SUPER(A);
int val;
}B;
看,这样就不用再去从新写基类的成员了,全部基类成员只在基类中定义一遍,在子类中经过宏来进行展开。
进阶5:模板?
咱们经过上面的说明,可以很快写出一个类的例子,这是一个能编译运行的C代码(linux gcc):
// Class.c
//
#include <stdio.h>
#include <stdlib.h>linux

//////////////////////////////////////////////////////
#define __SUPER(Base) \
union {\
 Base super; \
 struct {\
  Template_##Base\
  }; \
 }
//////////////////////////////////////////////////////
typedef struct A A;
struct A {
#define A_Template \
int data;\
int (*Val)(A* that, int a);
#define Template_A A_Template
Template_A
int val;
};
int Val(A* that, int a) {
 return that->data + a;
}
A * _A(A * that, int data) {
that->data = data;
that->Val = Val;
return that;
}
//////////////////////////////////////////////////////
typedef struct B {
__SUPER(A);
int val;
}B;
int myVal(B* that, int a) {
 return that->data * a;
}
B* _B(B* that, int val) {
_A(&that->super, val);
that->Val = myVal;
that->val = val;
}
//////////////////////////////////////////////////////
int main() {
 A a;
 _A(&a, 10);
 a.val = 1;
 printf("%d %d\n", a.val, a.Val(&a, 20));编程

 B b;
 _B(&b, 20);
 b.val = 2;
 printf("%d %d\n", b.val, b.Val(&b, 30));
 
 return 0;
}
能够看到结果是1 20和2 600,说明成功了。可是编译器报出警告:assignment from incompatible pointer type,这是由于基类Val是A类型参数,而B中重载时给的倒是B类型参数,因为是继承关系,而且是指针,因此不用去理会也不会有什么问题。可是若是真的要较真的话,就须要更改#define了:
struct A {
#define A_Template(T) \
int data;\
int (*Val)(T* that, int a);
#define Template_A(T) A_Template(struct T)
Template_A(A)
int val;
};
上面_SUPER和下面B调用__SUPER的地方也一并改掉,不赘述。能够看到,当在A中时,Val使用的A类型参数,而在B中时使用B类型参数,应该不会有问题了。--------但这并非模板!由于struct的局限性,咱们经过添加参数为函数传入that指针代替this指针,咱们定义类型T是为了除去继承时指针类型不匹配的问题, 但并无引入模板。
咱们在继承类中使用宏来展开基类,这一点跟模板很像,为何不能作成模板呢?哈哈,咱们能够模仿一下模板,但真正的模板并非这样的:
// Class.c
//
#include <stdio.h>
#include <stdlib.h>ide

//////////////////////////////////////////////////////
#define __SUPER(Base, Type, ...) \
union {\
 Base super; \
 struct {\
  Template_##Base(Type, __VA_ARGS__)\
  }; \
 }
//////////////////////////////////////////////////////
typedef struct A A;
struct A {
#define A_Template(T, Type) \
Type data;\
int (*Val)(T* that, int a);
#define Template_A(T, Type) A_Template(struct T, Type)
Template_A(A, int)
int val;
};
int Val(A* that, int a) {
 return that->data + a;
}
A * _A(A * that, int data) {
that->data = data;
that->Val = Val;
return that;
}
//////////////////////////////////////////////////////
typedef struct B {
__SUPER(A, B, int);
int val;
}B;
int myVal(B* that, int a) {
 return that->data * a;
}
B* _B(B* that, int val) {
_A(&that->super, val);
that->Val = myVal;
that->val = val;
}
//////////////////////////////////////////////////////
int main() {
 A a;
 _A(&a, 10);
 a.val = 1;
 printf("%d %d\n", a.val, a.Val(&a, 20));函数

 B b;
 _B(&b, 20);
 b.val = 2;
 printf("%d %d\n", b.val, b.Val(&b, 30));
 
 return 0;
}
看,模板就是#define template那里,而A在定义的时候就已经将模板特化成int类型,B在定义时将模板特化成int类型。结果仍旧同样。但这个仍旧不是模板,模板是在定义对象的时候特化,而这个是在定义类型时已经特化。
因为成员函数并非内联的(所谓内联,就是说每个对象包含的函数都是在对象内部扩展开的),因此这些函数必须写在外部,而外部必须保证struct进行了彻底定义,因此C是没有办法作到真正的模板的。
6.继承链
根据C的宏定义标准,宏是不能够嵌套的。所以若是要实现继承链,__SUPER会形成嵌套。所以,再增长一个___SUPER表示二级继承链,定义和__SUPER如出一辙,这样,___SUPER会调用Template而后调用__SUPER,构成继承链。若是还有更多继承,则继续定义____SUPER。另外,因为使用的是匿名union,所以基类的super都暴露在外面,继承链中会出现重复super定义,能够将super定义为super+基类名来区别。二级继承链定义以下,一级继承链也修改成下面的定义:
#define ___SUPER(Base, ...)\
union {\
 Base super##Base; \
 struct {\
  Template_##Base(__VA_ARGS__)\
  }; \
 }
好了,这样就能够实现多级继承链了。在_B构造函数中将super改成superA,而后添加C类继承B类(二级继承):
struct C {
___SUPER(B, C);
};
C * _C(C* that) {
_B(&that->superB, 77);
}
这样就构成了一个完整的继承链。完整可运行代码以下:
// Class.c
//
#include <stdio.h>
#include <stdlib.h>优化

typedef struct A A;
typedef struct B B;
typedef struct C C;
//////////////////////////////////////////////////////
#define __SUPER(Base, ...)\
union {\
 Base super##Base; \
 struct {\
  Template_##Base(__VA_ARGS__)\
  }; \
 }
#define ___SUPER(Base, ...)\
union {\
 Base super##Base; \
 struct {\
  Template_##Base(__VA_ARGS__)\
  }; \
 }
//////////////////////////////////////////////////////
struct A {
#define A_Template(T) \
int data;\
int (*Val)(T* that, int a);
#define Template_A(T) A_Template( T)
Template_A(A)
int val;
};
int Val(A* that, int a) {
 return that->data + a;
}
A * _A(A * that, int data) {
that->data = data;
that->Val = Val;
return that;
}
//////////////////////////////////////////////////////
struct B {
#define B_Template( T) \
__SUPER(A, T);\
int val;
#define Template_B( T) B_Template( T)
Template_B( B)
};
int myVal(B* that, int a) {
 return that->data * a;
}
B* _B(B* that, int val) {
_A(&that->superA, val);
that->Val = myVal;
that->val = val;
}
//////////////////////////////////////////////////////
struct C {
___SUPER(B, C);
};
C * _C(C* that) {
_B(&that->superB, 77);
}
//////////////////////////////////////////////////////
int main() {
 A a;
 _A(&a, 10);
 a.val = 1;
 printf("%d %d\n", a.val, a.Val(&a, 20));this

 B b;
 _B(&b, 20);
 b.val = 2;
 printf("%d %d\n", b.val, b.Val(&b, 30));
 
 C c;
 _C(&c);
 c.val = 3;
 printf("%d %d\n", c.val, c.Val(&c, 30));
 
 return 0;
}
输出结果:1 30 2 600 3 2310,结果正确。操作系统

7.结束语
虽然没能作到模板很遗憾,可是可以保证一些class的元素可以被使用在C语言中,已经很不错了。本次class的应用是在看操做系统内核时,寻找用C语言解决class问题时,其中的一些研究成果,而且在实践中已经证明可使用----虽然有些晦涩并且并非一个很好的编程体验(好比下面记载的,对于习惯了class的人来讲真的很难改变)
可是须要强调几点,这也是遗留的问题,以如今的能力仍是没有办法解决,期待高人来解决吧:
1.使用和定义类成员(函数)时必定要带上类对象指针that以代替this
2.struct的继承和C++的继承有很大区别,由于struct是经过在子类中从新定义基类来进行的,所以,基类设计必定要注意成员顺序,好比上面例子中A的val是放在最后而不是最前,这样经过子类继承时子类才不会将val继承过去。
3.因为宏定义的一些特性,致使继承链须要定义多个级别的__SUPER,须要明确当前继承是第几级,而后肯定使用哪一个__SUPER
4.第2点所带来的麻烦并不止这些,若是是连续继承,全部子类必须拥有基类的全部成员,继承链越靠后类就会变得越臃肿,这仅仅是指类所占的空间,然而在代码中使用__SUPER并不会看到这种放大做用
5.如上看到的,全部的成员函数并非内联函数,而仅仅是指向函数的指针,指向了一个struct外部的函数(在gcc中是不容许直接将函数写在struct中的,所以只能定义函数指针,其实也至关于一个成员变量而已),因此成员函数没有办法作内联优化。
完(*^_^*).net

stophin 2016/11/26
--------------------- 
做者:stophin 
来源:CSDN 
原文:https://blog.csdn.net/stophin/article/details/54646796 
版权声明:本文为博主原创文章,转载请附上博文连接!设计

相关文章
相关标签/搜索