c语⾔私有⽅法,C语⾔陷阱与技巧第28节,模拟“⾯向对象”编
程,怎样定义私有成员...
上⼀节讨论了结合指针和结构体语法,C语⾔也能实现“⾯向对象”编程。由此可以看出C语⾔是⼀门极其灵活的语⾔,简洁的语法即可实现复杂的程序。
C语⾔“对象”的成员变量
不过,在⾯向对象编程中,对象不仅仅有成员函数,也应该有成员变量。成员变量允许每⼀个对象都有独⽴存放数据的能⼒,各个对象的数据互不⼲扰。
int val = 0;struct cfun{void (*modify)();void (*print)();};void modify(){ val ++;}void myprint(){printf("val = %d\n", val);}struct cfun f1 = {modify, myprint};struct cfun f2 = {modify, myprint};f1.modify();f1.print();f2.print();在上⾯这段C语⾔代码中,为了
实例化类和实例化对象让“类”cfun 的各个成员函数都能访问变量 val,将 val 定义为全局变量了。但是 val 在内存中只有⼀份,所以就算是对象 f1 调⽤modify() 修改了 val 的值,f2.print() 打印的 val 也会被修改。
当然有些设计期望的结果就是如此。
如果不希望出现这种结果,似乎只能定义两个 val,或者将 val 定义成数组:
int val1 = 0, val2 = 0;//或者int val[2] = {0};
然后再修改使⽤到这些全局变量的C语⾔代码。但是这么做⾄少有三个不好的地⽅:
C语⾔程序常常不能事先知道该“类”究竟会被实例化成多少个对象,若是对象⽐较多,超过了 val 的数⽬,就会导致程序崩溃。这种⽅式使⽤起来也不⽅便,程序员还需再设计出⼀套映射规则,⽤于说明不同对象与各个全局变量的对应关系。全局变量的作⽤域⾮常⼤,使⽤时必须⼩⼼的处理(这可能包括防⽌误调⽤,防⽌数据不同步等)。可能有些读者看到这⾥,就会感叹“C语⾔果然不适合⾯向对象编程!”。
解决问题
其实要解决上述不⾜,只需要把变量加⼊“类”描述结构体就可以了,请看下⾯这段C语⾔代码:
struct cfun{void (*modify)();void (*print)();int val;};void modify(struct cfun *f){ f->val ++;}void myprint(struct cfun *f)
{printf("val = %d\n", f->val);}struct cfun f1 = {modify, myprint, 0};struct cfun f2 = {modify, myprint,
0};f1.modify(&f1);f1.print(&f1); // 输出 val = 1f2.print(&f2); // 输出 val = 0将 val 加⼊结构体 cfun,之后每实例化⼀个对象,就会⾃动为该对象分配⼀个 val 变量,各个对象的 val 变量是彼此独⽴的,互不影响。所以,f1.print() 之后会输出 “val = 1”,⽽
f2.print() 之后会输出“val = 0”。
现在唯⼀有些不⾜的是,在调⽤成员函数时,需要将对象指针传递进去,这主要是因为C语⾔没有原⽣的“对象”语法。当然,也有办法省去这⼀过程,只需再设计⼀套额外的处理机制就可以了(这⼀点以后有机会再说)。
不过,再设计⼀套额外的处理机制,显然会消耗额外的资源(如内存、cpu等),这与C语⾔程序的“使⽤最⼩的资源,最⾼效率的办事”精神相违背。⽽且,省去传递结构体指针的操作,并不会为C语⾔程序带来质的改变,所以⼀般不会实现这套机制。
事实上,Linux 内核源码中使⽤的“对象”也并未使⽤额外的处理避免传递对象指针。
C语⾔对象的“私有成员变量”
直接在类结构体中加⼊变量作为该类的成员变量是⽅便的,但是这种成员变量显然是 public 的,该类实例化的任意对象都能随意访问该变量。当然,如果本来就是如此设计的,这么做没有什么问题。
不过有时候,我们只希望某个成员变量只供类内部使⽤,也即希望该成员变量是 private 的,该怎么办呢?当然,最简单的办法就是写下⽂档告诉调⽤者不要随意访问该成员,但是这种⽅法不具备强制性,很多C语⾔程序员使⽤的 IDE 甚⾄会⾃动联想补全出该成员变量,⼀不⼩⼼,很容易就出现直接访问本来希望是 private 的成员变量。
其实,我们可以将类的私有(private)成员变量再做⼀次封装,在类定义中只保留⼀个指针⽤于索引各个成员变量即可。请看下⾯这段C语⾔代码:
struct cfun{void (*modify)();void (*print)();void *private_data;};// 不对外开放struct PRIVATE{char c;int val;//...};上述C语⾔代码将“类”cfun 的私有成员变量封装成⼀个结构体,并且在 cfun 的定义中只保留⼀个 void * 指针作为⼊⼝,解析私有成员变量的结构体struct PRIVATE 不对外开放,这样⼀来,只有在 cfun 内部才能解析出具体的私有成员变量。
外部调⽤者即使能够访问 private_data,也不能轻易的解析出具体的数据,这样就避免了外部调⽤者通过对象指针随意访问 cfun 的私有成员变量了。
对于 cfun 本⾝,结构体 struct PRIVATE 是可见的,因此访问 c 和 val 等私有成员变量是⽅便的,下⾯是⼀个⽰例,请看相关C语⾔代码:
void modify(struct cfun *f){ ((struct PRIVATE *)(f->private_data))->val ++;}void myprint(struct cfun *f){printf("val = %d\n", ((struct PRIVATE *)(f->private_data))->val);}
如果觉得 ((struct PRIVATE * )(f->private_data))->val 这样访问 val 太过繁琐,可以使⽤定义宏的⼩技巧,这⼀点我们已经⽐较熟悉了,例如:
#define PD(pcfun) \ ((struct PRIVATE *)((pcfun)->private_data))这样⼀来,再写C语⾔代码就简洁了:
void modify(struct cfun *f){ PD(f)->val ++;}void myprint(struct cfun *f){printf("val = %d\n", PD(f)->val);}⼩结
在使⽤C语⾔结构体和指针语法模拟⾯向对象编程时,也是允许定义结构体的成员变量的,本⽂讨论了两种⽅式:借助于全局变量,或者直接在结构体中添加变量。⽐较推荐后者,不过直接在结构体中添加的变量是 public 的,各个实例对象都能直接访问。如果希望定义类内部使⽤的 private 变量,可以借助C语⾔的结构体和指针语法再封装⼀层。
当然,本⽂所讨论的内容只属于抛砖引⽟,更多技巧和⽅法这⾥不可能⼀⼀涉及。相信读者在实际C语⾔项⽬开发中,必定能够发现更好的编程风格。
欢迎在评论区⼀起讨论,质疑。⽂章都是⼿打原创,每天最浅显的介绍C语⾔、linux等嵌⼊式开发,喜欢我的⽂章就关注⼀波吧,可以看到最新更新和之前的⽂章哦。
举报/反馈

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。