




已阅读5页,还剩5页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
C语言面向对象编程 原创作者: rubynroll 阅读:5643次 评论:0条 更新时间:2008-12-15 经常看到关于OO编程的讨论,C+, Java, C#.还有最近很流行的动态语言Python,Ruby等,但很少看到有C的份。在我看来,OO编程的核心是OO的思想,用什么语言倒是其次。但是,不可否认,那些专门为OO编程设计的语言可以比较方便和自然地表达OO思想,有些语言甚至强制使用OO特性。 C,作为最贴近底层的高级语言,拥有简洁的语法和直接内存操作能力(指针),大量运用于系统级编程,如操作系统内核,驱动程序等。而在嵌入式系统中,由于资源有限等因素,更倾向于用C编程。 C虽然在语言特性上并没有体现OO特性,但是依然可以通过各种编程技巧来体现OO的思想。由于C的高度自由的特点,在OO编程方面还能体现有别于其他语言的特殊韵味。 目 录 - 1. C的面向对象概念 2. 用struct来仿真class 3. 实现OO的继承机制 C的面向对象概念 OO Programing in C is not only POSSIBLE but also PRACTICAL. - OO思想在Unix世界中很早就有:UNIX把设备抽象成文件,这样就可以用一套相同的方法(open, read, write, close, . )去访问不同的设备和文件尽管设备之间的差异很大。用OO的观点来看,这些“设备”对象都实现了文件操作接口,可以想象有一个叫文件的基类,定义了文件操作接口,“设备”对象继承了“文件”对象.。在实现角度看,在内核里面,设备驱动提供了自己的read, write等实现,并用它们去填充文件操作结构体里面的函数指针.这和C+里面的虚函数运行时绑定的道理是一样的。( C+虚函数是其实是运行时静态绑定,而文件操作接口可以运行时动态绑定 :-) Linux内核中则处处体现了OO的思想。2.6内核的Device Driver Modal是一套层次分明又错综复杂的机制,其中体现了许多OO设计理念。虽然可能设备驱动程序开发者觉察不到,但所有的设备驱动对象内部都隐藏了一个叫 KObject的对象。内核把这些KObjects互相联系在一起,并通过KObject的相互关系构造了/sys文件系统。/sys就是内核中各种设备对象的映射图,如果把/sys全部展开,我们可以清楚地看到各种对象的关系。 实践证明,C也可以很好地用于OO编程,而且可以用于构造很复杂的系统,而且C在表达OO思想的时候并不会显得蹩脚,而是可以很简单,很自然。 用struct来仿真class OO Programing in C is not only POSSIBLE, but also PRACTICAL. - “class“是很多OO编程语言里的关键字,它来源于OO鼻祖Smalltalk。class(类),是对一群有相同特性的对象的抽象概括,对象称为类的实例。在class里面可以存放有状态(变量),行为(函数/方法).有关OO概念、方法的文章太多了,不再啰嗦。在C里面,唯一可以实现自定义类型的是struct,struct是C的OO编程最重要的工具。 一个最常见的技巧,就是用struct来仿真class: 在struct里面放入变量,函数指针,嵌入其他struct等。 以下例子摘自我最近刚开发完成的一个USB Firmware项目: C代码 1. structusb_device; 2. structusb_ctl; 3. 4. structusb_iobuf 5. intlen;/*datalengthinthebuffer*/6. unsignedcharbufUSBEPFIFO_SIZE;/*databufferitself*/7. ; 8. 9. structusb_endpointinttype;/*endpointtype:BULKIN,BULKOUT,CTL,ISO.*/10. intqlen;/*queuelength*/11. 12. xQueueHandlelock;/*semaphorelock*/13. xQueueHandleq;/*dataqueue(pointerofbulk_buf)*/14. 15. intidx;/*endpointindex*/16. intepx;/*endpointmarkbit*/17. intcfg;/*endpointconfigure*/18. intbank;/*currentoperationbank(forping-pongmode)*/19. inttxCount;/*usedforping-pongmode*/*endpointdataprocessfunction*/void(*ep_process)(structusb_device*dev,structusb_endpoint*ep,xISRStatus*pxMessage); 20. ; 21. 22. structusb_descriptor 23. inttype;/*descriptortype:device,conf,stringorendpoint*/24. intidx;/*descriptorindex(forstringdescriptor)*/25. intsize;/*descriptorsize*/26. void*data;/*descriptordata*/27. structlist_headlist;/*linklistofdescriptors*/28. ; 29. 30. structusb_deviceOps 31. int(*init)(structusb_device*dev);/*calledwhenframeworkinitusbdevice,adddevicedescriptors,initprivatedata.etc.*/32. int(*reset)(structusb_device*dev);/*calledwhenresetedbyhost*/33. int(*switch_in)(structusb_device*dev);/*calledwhenswitchin*/34. int(*switch_out)(structusb_device*dev);/*calledwhenswithcout*/*calledwhenHOSTrequestclassinterfacedata*/35. void(*class_interface_req)(structusb_device*dev,xUSB_REQUEST*pxRequest);/*calledwhenHOSTcompletethedatasendingstage*/int(*ctl_data_comp)(structusb_device*dev,xCONTROL_MESSAGE*pxMessage); 36. ; 37. 38. structusb_ctlOps 39. void(*ctl_transmit_null)(structusb_ctl*ctl); 40. void(*ctl_send_stall)(structusb_ctl*ctl); 41. void(*ctl_reset_ep0)(structusb_ctl*ctl); 42. void(*ctl_detach_usb)(structusb_ctl*ctl); 43. void(*ctl_attach_usb)(structusb_ctl*ctl); 44. void(*ctl_send_data)(structusb_ctl*ctl,unsignedchar*data, 45. intreq_len, 46. intsend_len, 47. intis_des); 48. ; 49. 50. 51. structusb_ctl 52. intaddr;/*addressallocedbyhost*/53. intconf;/*configurationsetbyhost*/54. eDRIVER_STATEstate;/*currentstatus*/55. xCONTROL_MESSAGEtx;/*controltransmitmessage*/56. xCONTROL_MESSAGErx;/*controlreceivemessage*/57. structubufm*bufmn;/*usb_iobufbuffermanager,sharedbyallusbdevices*/58. intprio;/*themaintaskpriority*/59. xTaskHandletask_handle;/*themaintaskhandler*/60. structusb_ctlOps*ctlOps;/*controlendpointoperations*/61. ; 62. 63. structusb_device 64. charname16;/*devicename,e.g.usbser*/65. structusb_deviceOps*ops;/*usbdevicecallbackfunctions*/66. 67. structusb_ctl*ctl;/*usbcontrolenpoint,providedbyframework*/68. structlist_headdesc_list;/*usbdescriptors*/69. structusb_endpoint*epMAX_ENDPOINTS;/*endpoints*/70. intactive;/*whetherthedeviceisactive*/71. xQueueHandleready;/*notifythisqueuewhenusbdeviceready*/72. void*private;/*deviceprivatedata*/73. structlist_headlist;/*linklistofusbdevice*/74. ;struct usb_device;struct usb_ctl;struct usb_iobuf int len;/* data length in the buffer */unsigned char bufUSBEPFIFO_SIZE;/* data buffer itself */;struct usb_endpoint int type;/* endpoint type: BULKIN, BULKOUT, CTL, ISO . */int qlen;/* queue length */xQueueHandle lock;/* semaphore lock */xQueueHandle q;/* data queue (pointer of bulk_buf) */int idx;/* endpoint index */int epx;/* endpoint mark bit */int cfg;/* endpoint configure */int bank;/* current operation bank (for ping-pong mode) */int txCount;/* used for ping-pong mode */* endpoint data process function */void (*ep_process) (struct usb_device *dev, struct usb_endpoint *ep, xISRStatus *pxMessage);struct usb_descriptor int type;/* descriptor type: device, conf, string or endpoint */int idx;/* descriptor index (for string descriptor) */int size;/* descriptor size */void * data;/* descriptor data */struct list_head list;/* link list of descriptors */;struct usb_deviceOps int (*init)(struct usb_device *dev);/* called when framework init usb device, add device descriptors, init private data . etc. */int (*reset)(struct usb_device *dev);/* called when reseted by host */int (*switch_in)(struct usb_device *dev);/* called when switch in */int (*switch_out)(struct usb_device *dev);/* called when swithc out */* called when HOST request class interface data */void (*class_interface_req)(struct usb_device *dev, xUSB_REQUEST *pxRequest);/* called when HOST complete the data sending stage */int (*ctl_data_comp)(struct usb_device *dev, xCONTROL_MESSAGE *pxMessage);struct usb_ctlOps void (*ctl_transmit_null)(struct usb_ctl *ctl);void (*ctl_send_stall)(struct usb_ctl *ctl);void (*ctl_reset_ep0)(struct usb_ctl *ctl);void (*ctl_detach_usb)(struct usb_ctl *ctl);void (*ctl_attach_usb)(struct usb_ctl *ctl);void (*ctl_send_data)(struct usb_ctl *ctl, unsigned char *data,int req_len,int send_len,int is_des);struct usb_ctl int addr;/* address alloced by host */int conf;/* configuration set by host */eDRIVER_STATE state;/* current status */xCONTROL_MESSAGE tx;/* control transmit message */xCONTROL_MESSAGE rx;/* control receive message */struct ubufm *bufmn;/* usb_iobuf buffer manager, shared by all usb devices */int prio;/* the main task priority */xTaskHandle task_handle;/* the main task handler */struct usb_ctlOps *ctlOps;/* control endpoint operations */;struct usb_device char name16;/* device name, e.g. usbser */struct usb_deviceOps *ops;/* usb device callback functions */struct usb_ctl *ctl;/* usb control enpoint, provided by framework */struct list_head desc_list;/* usb descriptors */struct usb_endpoint *epMAX_ENDPOINTS;/* endpoints */int active;/* whether the device is active */xQueueHandle ready;/* notify this queue when usb device ready */void *private;/* device private data */struct list_head list;/* link list of usb device */;在这个例子,我用struct分别描述了USB设备,USB控制通道,USB端点,USB描述符和USB缓冲区对象。USB设备对象包含了若干个USB端点,一个USB控制通道指针,一个USB描述符表的表头(指向若干个USB描述符),和一个USB缓冲区管理对象。而且,USB设备对象还包含了name属性,一个由USB Framework调用的回调函数集,还有一个用于连接其他USB设备的链表节点。 值得一提的是,USB设备对象中有一个void *private 成员,可以指向任何数据。实际上在我的程序里,我实现了usb-serial和usb-mass-storage两个USB设备,对于usb-serial对象,private我弃之不用,而在usb-mass-storage对象中,private指向一个Mass storage对象,usb-mass-storage正是通过这个Mass storage对象访问外部大容量存储的(在我的程序里,Mass storage对象和一个MMC Card对象绑定,外部存储是SD/MMC卡)。由于对于每一种设备的具体实现来说,它知道private指向的是何种类型的设备,因此不会引起混乱。而外部程序根据需要在初始化USB设备对象前赋予private有意义的值运行时动态绑定。 这一系列struct基本上如实地反映了USB DEVICE硬件逻辑和规范要求: 一个USB设备包含若干个端点,其中有一个固定的控制端点(端点0)。在枚举阶段USB设备要根据HOST的请求应答相应的描述符. 现在回到OO的话题,这个例子中体现了组合类:USB设备对象包含了USB端点对象,USB描述符对象.。还有动态绑定 (private成员)。从严格的OO意义上来看,好像有点怪,不过我认为这恰恰是C的特点简洁,直接。不信你用C+表达试试?也许会更漂亮,很OO,但是不一定会如此清爽! P.S. : 熟悉USB Firmware开发的人可能对struct usb_endpoint中的epx,cfg,bank和txCount四个成员有异议,因为这些成员是和特定的硬件相关,并不是所有的USB硬件都支持ping-pong mode,所以bank和txCount不一定用得上,epx, cfg也可能因硬件的不同而不同。没错!更理想的设计是把与硬件相关的部分分离出来,用void *private指向各自的与硬件相关的配置就像struct usb_device所采用方法,所以更好的版本应该是: Java代码 1. structusb_endpoint 2. inttype;/*endpointtype:BULKIN,BULKOUT,CTL,ISO.*/3. intqlen;/*queuelength*/4. xQueueHandlelock;/*semaphorelock*/5. xQueueHandleq;/*dataqueue(pointerofbulk_buf)*/6. intidx;/*endpointindex*/7. 8. /*endpointdataprocessfunction*/9. void(*ep_process)(structusb_device*dev,structusb_endpoint*ep,xISRStatus*pxMessage); 10. void*private;/*endpointprivatedata(hardwarerelevant)*/11. ;struct usb_endpoint int type;/* endpoint type: BULKIN, BULKOUT, CTL, ISO . */ int qlen;/* queue length */ xQueueHandle lock;/* semaphore lock */ xQueueHandle q;/* data queue (pointer of bulk_buf) */ int idx;/* endpoint index */ /* endpoint data process function */ void (*ep_process)(struct usb_device *dev,struct usb_endpoint *ep, xISRStatus *pxMessage); void *private;/* endpoint private data (hardware relevant) */;tips: 用C表达的一个关键处就是要很好地应用struct来描述模型。 实现OO的继承机制 OO Programing in C is not only POSSIBLE but also PRACTICAL - OO的一个亮点是类的继承,通过继承,可以重用许多代码。而且继承也是现实生活中非常自然的一种关系。但是很不幸,C没有class,更没有提供继承的表达方式。既然能用C的struct来仿真class, 那能不能继续来仿真继承呢?答案是:possible。就像书中所叙述的那样你可以用C来达到所有C+能做到的事。但这种仿真显然毫无实际应用价值。 继承是一种表达方式,代码重用才是目的。 为了重用代码,C+可以用继承的方式来巧妙的达到目的,但是也必须付出代价:你必须非常仔细地设计你的类族谱,要有前瞻性,要有可扩展性,要决定分多少个层次.这些都不是容易做到的事。 C别无选择,模块化设计,函数,宏.只能通过巧妙的设计才能达到代码可重用的目的。还是举个例子来说明C是如何做到殊途同归的吧。 链表是一个非常常用的数据结构,常用于管理无序的数据(对象)集合。链表操作,特别是双向链表操作很容易出错。重用一套通用操作链表的代码可以为我们省不少事。在C+中,我们可以用经典的STL中的list类。为了适应各种数据类型,list类用模板来实现。list类实现的很巧妙,功能很强,但是,不得不说,很少人用。其实不仅list类很少用,STL都很少人用。(希望这是我的一家之言,反正我所熟悉的C+程序员都不怎么用STL :-)当然在C+中你还有另外一个选择:实现一个List基类完成链表操作,要放入链表的类从List类继承而来,就拥有了一套操作list的方法。 Linux内核中用C提供了一套非常巧妙的方法操作链表,位于./linux/include/linux/list.h,只用一些宏和inline函数来实现双向链表。摘抄一部分出来: C代码 1. . 2. structlist_head 3. structlist_head*next,*prev; 4. ; 5. 6. 7. #defineLIST_HEAD_INIT(name)&(name),&(name) 8. 9. #defineLIST_HEAD(name) 10. structlist_headname=LIST_HEAD_INIT(name) 11. 12. #defineINIT_LIST_HEAD(ptr)do 13. (ptr)-next=(ptr);(ptr)-prev=(ptr); 14. while(0) 15. 16. /* 17. *Insertanewentrybetweentwoknownconsecutiveentries. 18. * 19. *Thisisonlyforinternallistmanipulationwhereweknow 20. *theprev/nextentriesalready! 21. */22. staticinlinevoid_list_add(structlist_head*new, 23. structlist_head*prev, 24. structlist_head*next) 25. 26. next-prev=new; 27. new-next=next; 28. new-prev=prev; 29. prev-next=new; 30. 31. 32. /* 33. *list_add-addanewentry 34. *new:newentrytobeadded 35. *head:listheadtoadditafter 36. * 37. *Insertanewentryafterthespecifiedhead. 38. *Thisisgoodforimplementingstacks. 39. */40. staticinlinevoidlist_add(structlist_head*new,structlist_head*head) 41. 42. _list_add(new,head,head-next); 43. 44. 45. . 46. 47. /* 48. *list_entry-getthestructforthisentry 49. *ptr:the&structlist_headpointer. 50. *type:thetypeofthestructthisisembeddedin. 51. *member:thenameofthelist_structwithinthestruct. 52. */53. #definelist_entry(ptr,type,member) 54. container_of(ptr,type,member) 55. 56. /* 57. *list_for_each-iterateoveralist 58. *pos:the&structlist_headtouseasaloopcounter. 59. *head:theheadforyourlist. 60. */61. #definelist_for_each(pos,head) 62. for(pos=(head)-next;prefetch(pos-next),pos!=(head); 63. pos=pos-next) 64. 65. .struct list_head struct list_head *next, *prev;#define LIST_HEAD_INIT(name) &(name), &(name) #define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)#define INIT_LIST_HEAD(ptr) do (ptr)-next = (ptr); (ptr)-prev = (ptr); while (0)/* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */static inline void _list_add(struct list_head *new, struct list_head *prev, struct list_head *next) next-prev = new; new-next = next; new-prev = prev; prev-next = new;/* * list_add - add a new entry * new: new entry to be added * head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */static inline void list_add(struct list_head *new, struct list_head *head) _list_add(new, head, head-next);./* * list_entry - get the struct for this entry * ptr: the &struct list_head pointer. * type: the type of the struct this is embedded in. * member: the name of the list_struct within the struct. */#define list_entry(ptr, type, member) container_of(ptr, type, member)/* * list_for_each - iterate over a list * pos: the &struct list_head to use as a loop counter. * head: the head for your list. */#define list_for_each(pos, head) for (pos = (head)-next; prefetch(pos-next), pos != (head); pos = pos-next).其中 container_of 宏如下: C代码 1. /* 2. *container_of-castamemberofastructureouttothecontainingstructure 3. *ptr:thepointertothemember. 4. *type:thetypeofthecontainerstructthisisembeddedin. 5. *member:thenameofthememberwithinthestruct. 6. * 7. */8. #definecontainer_of(ptr,type,member)( 9. consttypeof(type*)0)-member)*_mptr=(ptr); 10. (type*)(char*)_mptr-offsetof(type,member);)/* * container_of - cast a member of a structure out to the containing structure * ptr: the pointer to the member. * type: the type of the container struct this is embedded in. * member: the name of the member within the struct. * */#define container_of(ptr, type, member) ( const typeof( (type *)0)-member ) *_mptr = (ptr); (type *)( (char *)_mptr - offsetof(type,member) );)这里使用了GCC特有的 typeof 关键字,如果想用其他编译器也想编译通过的话,可以修改成: C代码 1. #definecontainer_of(ptr,type,member)( 2. (type*)(char*)ptr-offsetof(type,member)#define container_of(ptr, type, member) ( (type *)( (char *)ptr - offsetof(type,member) ) )为了便于说明,prefetch定义成: Java代码 1. staticinlinevoidprefetch(constvoid*x);static inline void prefetch(const void *x) ;offsetof的一个简单版本: C代码 1. #de
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年事业单位工勤技能-江苏-江苏垃圾清扫与处理工二级(技师)历年参考题库含答案解析(5套)
- 2025年事业单位工勤技能-新疆-新疆造林管护工二级(技师)历年参考题库含答案解析(5套)
- 2025年事业单位工勤技能-广西-广西水工监测工一级(高级技师)历年参考题库含答案解析
- 2025年事业单位工勤技能-广西-广西广播电视天线工三级(高级工)历年参考题库含答案解析
- 2025年事业单位工勤技能-广西-广西园林绿化工一级(高级技师)历年参考题库典型考点含答案解析
- 2025年事业单位工勤技能-广东-广东计算机文字录入处理员二级(技师)历年参考题库含答案解析
- 2025年事业单位工勤技能-广东-广东水文勘测工三级(高级工)历年参考题库典型考点含答案解析
- 2025年事业单位工勤技能-安徽-安徽土建施工人员五级(初级工)历年参考题库典型考点含答案解析
- 2020-2025年初级经济师之初级经济师财政税收高分通关题库A4可打印版
- 2025年事业单位工勤技能-北京-北京不动产测绘员五级(初级工)历年参考题库典型考点含答案解析
- 广东省广州市2024-2025学年高一上学期开学考试英语检测试题(附答案)
- 附件3:公司境外突发事件应急预案
- 3.1平均数(教学课件)五年级数学上册 沪教版
- 《车路协同系统装调与测试》 课件全套 吕丕华 任务1-10 装调工作方案制定- 绿波车速引导系统测试与设置
- 高端案场物业服务方案
- 初中英语比较级和最高级专项练习题含答案
- 光伏焊带完整版本
- 双镜联合手术案例分享
- 质量通病案例课件
- 南航SVC大数据支撑案例
- 2024新课标《义务教育数学课程标准(2022年版)》测试题(含答案)
评论
0/150
提交评论