编程语言应用

首页 » 常识 » 问答 » C语言陷阱与技巧第28节,模拟面向对象
TUhjnbcbe - 2024/6/4 18:13:00

上一节讨论了结合指针和结构体语法,C语言也能实现“面向对象”编程。由此可以看出C语言是一门极其灵活的语言,简洁的语法即可实现复杂的程序。

C语言“对象”的成员变量

不过,在面向对象编程中,对象不仅仅有成员函数,也应该有成员变量。成员变量允许每一个对象都有独立存放数据的能力,各个对象的数据互不干扰。

intval=0;structcfun{void(*modify)();void(*print)();};voidmodify(){val++;}voidmyprint(){printf(val=%d\n,val);}structcfunf1={modify,myprint};structcfunf2={modify,myprint};f1.modify();f1.print();f2.print();在上面这段C语言代码中,为了让“类”cfun的各个成员函数都能访问变量val,将val定义为全局变量了。但是val在内存中只有一份,所以就算是对象f1调用modify()修改了val的值,f2.print()打印的val也会被修改。

当然有些设计期望的结果就是如此。

如果不希望出现这种结果,似乎只能定义两个val,或者将val定义成数组:

intval1=0,val2=0;//或者intval[2]={0};

然后再修改使用到这些全局变量的C语言代码。但是这么做至少有三个不好的地方:

C语言程序常常不能事先知道该“类”究竟会被实例化成多少个对象,若是对象比较多,超过了val的数目,就会导致程序崩溃。这种方式使用起来也不方便,程序员还需再设计出一套映射规则,用于说明不同对象与各个全局变量的对应关系。全局变量的作用域非常大,使用时必须小心的处理(这可能包括防止误调用,防止数据不同步等)。可能有些读者看到这里,就会感叹“C语言果然不适合面向对象编程!”。

解决问题

其实要解决上述不足,只需要把变量加入“类”描述结构体就可以了,请看下面这段C语言代码:

structcfun{void(*modify)();void(*print)();intval;};voidmodify(structcfun*f){f-val++;}voidmyprint(structcfun*f){printf(val=%d\n,f-val);}structcfunf1={modify,myprint,0};structcfunf2={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语言代码:

structcfun{void(*modify)();void(*print)();void*private_data;};//不对外开放structPRIVATE{charc;intval;//...};上述C语言代码将“类”cfun的私有成员变量封装成一个结构体,并且在cfun的定义中只保留一个void*指针作为入口,解析私有成员变量的结构体structPRIVATE不对外开放,这样一来,只有在cfun内部才能解析出具体的私有成员变量。

外部调用者即使能够访问private_data,也不能轻易的解析出具体的数据,这样就避免了外部调用者通过对象指针随意访问cfun的私有成员变量了。

对于cfun本身,结构体structPRIVATE是可见的,因此访问c和val等私有成员变量是方便的,下面是一个示例,请看相关C语言代码:

voidmodify(structcfun*f){((structPRIVATE*)(f-private_data))-val++;}voidmyprint(structcfun*f){printf(val=%d\n,((structPRIVATE*)(f-private_data))-val);}

如果觉得((structPRIVATE*)(f-private_data))-val这样访问val太过繁琐,可以使用定义宏的小技巧,这一点我们已经比较熟悉了,例如:

#definePD(pcfun)\((structPRIVATE*)((pcfun)-private_data))这样一来,再写C语言代码就简洁了:

voidmodify(structcfun*f){PD(f)-val++;}voidmyprint(structcfun*f){printf(val=%d\n,PD(f)-val);}小结

在使用C语言结构体和指针语法模拟面向对象编程时,也是允许定义结构体的成员变量的,本文讨论了两种方式:借助于全局变量,或者直接在结构体中添加变量。比较推荐后者,不过直接在结构体中添加的变量是public的,各个实例对象都能直接访问。如果希望定义类内部使用的private变量,可以借助C语言的结构体和指针语法再封装一层。

当然,本文所讨论的内容只属于抛砖引玉,更多技巧和方法这里不可能一一涉及。相信读者在实际C语言项目开发中,必定能够发现更好的编程风格。

欢迎在评论区一起讨论,质疑。文章都是手打原创,每天最浅显的介绍C语言、linux等嵌入式开发,喜欢我的文章就

1
查看完整版本: C语言陷阱与技巧第28节,模拟面向对象