• C语言也可以“面向对象”—— C语言实现封装、继承和多态


     

    网上看到一篇讲述C语言面向对象的文章,顿时眼睛一亮,也佩服作者认识的深入。

    特此转来与大家分享,网址: http://dongxicheng.org/cpp/ooc/

    1、  概述

    C语言是一种面向过程的程序设计语言,而C++是在C语言基础上衍生来了的面向对象的语言,实际上,很多C++实现的底层是用C语言实现的,如在Visual C++中的Interface其实就是struct,查找Interface的定义,你可以发现有这样的宏定义:

    #ifndef Interface

    #define Interface struct

    #endif

    C++在语言级别上添加了很多新机制(继承,多态等),而在C语言中,我们也可以使用这样的机制,前提是我们不得不自己实现。

    本文介绍了用C语言实现封装,继承和多态的方法。

    2、  基本知识

    在正式介绍C语言实现封装,继承和多态事前,先介绍一下C语言中的几个概念和语法。

    (1)    结构体

    在C语言中,常把一个对象用结构体进行封装,这样便于对对象进行操作,比如:

    [cpp] view plain copy
     
    1. strcut Point{  
    2.    
    3. int x;  
    4.    
    5. int y;  
    6.    
    7. };  

    结构体可以嵌套。因而可以把一个结构体当成另一个结构体的成员,如:
    [cpp] view plain copy
     
    1. struct Circle {  
    2.    
    3. struct Point point_;  
    4.    
    5. int radius;  
    6.    
    7. };  
    该结构体与以下定义完全一样(包括内存布置都一样):
    [cpp] view plain copy
     
    1. struct Circle {  
    2.    
    3. int x;  
    4.    
    5. int y;  
    6.    
    7. int radius;  
    8.    
    9. };  

    (2)    函数指针

    函数指针是指针的一种,它指向函数的首地址(函数的函数名即为函数的首地址),可以通过函数指针来调用函数。

    如函数:

    int func(int a[], int n);

    可以这样声明函数指针:

    int (*pFunc)(int a[], int n);

    这样使用:

    pFunc = func;

    (*pFunc)(a, n);【或者PFunc(a, n)】

    可以用typedef定义一个函数指针类型,如:

    typdef int (*FUNC)(int a[], int n)

    可以这样使用:

    int cal_a(FUNC fptr, int a[], int n)

    {

    //实现体

    }

    (3)    extern与static

    extern和static是C语言中的两个修饰符,extern可用于修饰函数或者变量,表示该变量或者函数在其他文件中进行了定义;static也可用于修饰函数或者变量,表示该函数或者变量只能在该文件中使用。可利用它们对数据或者函数进行隐藏或者限制访问权限。

    3、  封装

    在C语言中,可以用结构+函数指针来模拟类的实现,而用这种结构定义的变量就是对象。

    封装的主要含义是隐藏内部的行为和信息,使用者只用看到对外提供的接口和公开的信息。有两种方法实现封装:

    (1)    利用C语言语法。在头文件中声明,在C文件中真正定义它。

    这样可以隐藏内部信息,因为外部不知道对象所占内存的大小,所以不能静态的创建该类的对象,只能调用类提供的创建函数才能创建。这种方法的缺陷是不支持继承,因为子类中得不到任何关于父类的信息。如:

    [cpp] view plain copy
     
    1. //头文件:point.h  
    2.    
    3. #ifndef POINT_H  
    4.    
    5. #define POINT_H  
    6.    
    7. struct Point;  
    8.    
    9. typedef struct Point point;  
    10.    
    11. point * new_point(); //newer a point object  
    12.    
    13. void free_point(point *point_);// free the allocated space  
    14.    
    15. #endif  
    16.    
    17. //C文件:point.c  
    18.    
    19. #include”point.h”  
    20.    
    21. strcut Point  
    22.    
    23. {  
    24.    
    25. int x;  
    26.    
    27. int y;  
    28.    
    29. };  
    30.    
    31. point * new_point()  
    32.    
    33. {  
    34.    
    35. point * new_point_ = (point *) malloc(sizeof(point));  
    36.    
    37. return new_point_;  
    38.    
    39. }  
    40.    
    41. void free_point(point *point_)  
    42.    
    43. {  
    44.    
    45. if(point_ == NULL)  
    46.    
    47. return;  
    48.    
    49. free(point_);  
    50.    
    51. }  

    (2)    把私有数据信息放在一个不透明的priv变量或者结构体中。只有类的实现代码才知道priv或者结构体的真正定义。如:
    [cpp] view plain copy
     
    1. #ifndef POINT _H  
    2.    
    3. #define POINT_H  
    4.    
    5. typedef struct Point point;  
    6.    
    7. typedef struct pointPrivate pointPrivate;  
    8.    
    9. strcut Point  
    10.    
    11. {  
    12.    
    13. Struct pointPrivate *pp;  
    14.    
    15. };  
    16.    
    17. int get_x(point *point_);  
    18.    
    19. int get_y(point *point_);  
    20.    
    21. point * new_point(); //newer a point object  
    22.    
    23. void free_point(point *point_);// free the allocated space  
    24.    
    25. #endif  
    26.    
    27. //C文件:point.c  
    28.    
    29. #include”point.h”  
    30.    
    31. struct pointPrivate  
    32.    
    33. {  
    34.    
    35. int x;  
    36.    
    37. int y;  
    38.    
    39. }  
    40.    
    41. int get_x(point *point_)  
    42.    
    43. {  
    44.    
    45. return point_->pp->x;  
    46.    
    47. }  
    48.    
    49. int get_y(point *point_)  
    50.    
    51. {  
    52.    
    53. return point_->pp->y;  
    54.    
    55. }  
    56.    
    57. //others…..  

    4、  继承

    在C语言中,可以利用“结构在内存中的布局与结构的声明具有一致的顺序”这一事实实现继承。

    比如我们要设计一个作图工具,其中可能涉及到的对象有Point(点),Circle(圆),由于圆是由点组成的,所有可以看成Circle继承自Point。

    另外,Point和Circle都需要空间申请,空间释放等操作,所有他们有共同的基类Base。

    [cpp] view plain copy
     
    1. //内存管理类new.h  
    2.    
    3. #ifndef NEW_H  
    4.    
    5. #define NEW_H  
    6.    
    7. void * new (const void * class, ...);  
    8.    
    9. void delete (void * item);  
    10.    
    11. void draw (const void * self);  
    12.    
    13. #endif  
    14.    
    15. //内存管理类的C文件:new.c  
    16.    
    17. #include “new.h”  
    18.    
    19. #include “base.h”  
    20.    
    21. void * new (const void * _base, ...)  
    22.    
    23. {  
    24.    
    25. const struct Base * base = _base;  
    26.    
    27. void * p = calloc(1, base->size);  
    28.    
    29. assert(p);  
    30.    
    31. * (const struct Base **) p = base;  
    32.    
    33. if (base ->ctor)  
    34.    
    35. {  
    36.    
    37. va_list ap;  
    38.    
    39. va_start(ap, _base);  
    40.    
    41. p = base ->ctor(p, &ap);  
    42.    
    43. va_end(ap);  
    44.    
    45. }  
    46.    
    47. return p;  
    48.    
    49. }  
    50.    
    51. void delete (void * self)  
    52.    
    53. {  
    54.    
    55. const struct Base ** cp = self;  
    56.    
    57. if (self && * cp && (* cp) —> dtor)  
    58.    
    59. self = (* cp) —>dtor(self);  
    60.    
    61. free(self);  
    62.    
    63. }  
    64.    
    65. void draw (const void * self)  
    66.    
    67. {  
    68.    
    69. const struct Base * const * cp = self;  
    70.    
    71. assert(self &&* cp && (* cp)->draw);  
    72.    
    73. (* cp) ->draw(self);  
    74.    
    75. }  
    76.    
    77. //基类:base.h  
    78.    
    79. #ifndef BASE_H  
    80.    
    81. #define BASE_H  
    82.    
    83. struct Base  
    84.    
    85. {  
    86.    
    87. size_t size; //类所占空间  
    88.    
    89. void * (* ctor) (void * self, va_list * app); //构造函数  
    90.    
    91. void * (* dtor) (void * self); //析构函数  
    92.    
    93. void (* draw) (const void * self); //作图  
    94.    
    95. };  
    96.    
    97. #endif  
    98.    
    99. //Point头文件(对外提供的接口):point.h  
    100.    
    101. #ifndef   POINT_H  
    102.    
    103. #define  POINT_H  
    104.    
    105. extern const void * Point;                /* 使用方法:new (Point, x, y); */  
    106.    
    107. #endif  
    108.    
    109. //Point内部头文件(外面看不到):point.r  
    110.    
    111. #ifndef POINT_R  
    112.    
    113. #define POINT_R  
    114.    
    115. struct Point  
    116.    
    117. {  
    118.    
    119. const void * base; //继承,基类指针,放在第一个位置,const是防止修改  
    120.    
    121. int x, y;        //坐标  
    122.    
    123. };  
    124.    
    125. #endif  
    126.    
    127. //Point的C文件:point.c  
    128.    
    129. #include “point.h”  
    130.    
    131. #include “new.h”  
    132.    
    133. #include “point.h”  
    134.    
    135. #include “point.r”  
    136.    
    137. static void * Point_ctor (void * _self, va_list * app)  
    138.    
    139. {  
    140.    
    141. struct Point * self = _self;  
    142.    
    143. self ->x = va_arg(* app, int);  
    144.    
    145. self ->y = va_arg(* app, int);  
    146.    
    147. return self;  
    148.    
    149. }  
    150.    
    151. static void Point_draw (const void * _self)  
    152.    
    153. {  
    154.    
    155. const struct Point * self = _self;  
    156.    
    157. printf(“draw (%d,%d)”, self -> x, self -> y);  
    158.    
    159. }  
    160.    
    161. static const struct Base _Point = {  
    162.    
    163. sizeof(struct Point), Point_ctor, 0, Point_draw  
    164.    
    165. };  
    166.    
    167. const void * Point = & _Point;  
    168.    
    169. //测试程序:main.c  
    170.    
    171. #include “point.h”  
    172.    
    173. #include “new.h”  
    174.    
    175. int main (int argc, char ** argv)  
    176.    
    177. {  
    178.    
    179. void * p = new(Point, 1, 2);  
    180.    
    181. draw(p);  
    182.    
    183. delete(p);  
    184.    
    185. }  

    同样,Circle要继承Point,则可以这样:
    [cpp] view plain copy
     
    1. struct Circle  
    2.    
    3. {  
    4.    
    5. const struct Point point; //放在第一位,可表继承  
    6.    
    7. int radius;  
    8.    
    9. };  

    5、  多态

    可以是用C语言中的万能指针void* 实现多态,接上面的例子:

    [cpp] view plain copy
     
    1. //测试main.c  
    2.    
    3. void * p = new(Point, 1, 2);  
    4.    
    5. void * pp = new(Circle, 1, 2);  
    6.    
    7. draw(p); //draw函数实现了多态  
    8.    
    9. draw(pp);  
    10.    
    11. delete(p);  
    12.    
    13. delete(pp);  

    6、  总结

    C语言能够模拟实现面向对象语言具有的特性,包括:多态,继承,封装等,现在很多开源软件都了用C语言实现了这几个特性,包括大型开源数据库系统postgreSQL,可移植的C语言面向对象框架GObject,无线二进制运行环境BREW。采用C语言实现多态,继承,封装,能够让软件有更好的可读性,可扩展性。

    7、  参考资料

    (1)        《C语言中extern和static用法》:

    http://www.cnblogs.com/hishope/archive/2008/08/28/1278822.html

    (2)        《三、使用GObject——私有成员和静态变量》:

    http://blog.csdn.net/wormsun/archive/2009/11/25/4874465.aspx

    (3)        《技巧:用 C 语言实现程序的多态性》:

    http://www.ibm.com/developerworks/cn/linux/l-cn-cpolym/index.html?ca=drs-

    (4)        书籍《Object-Oriented Programming With ANSI-C》

    8、  代码下载

    本文中的代码可以在此处下载:代码下载

    原创文章,转载请注明: 转载自董的博客

    本文链接地址: http://dongxicheng.org/cpp/ooc/

    作者:Dong,作者介绍:http://dongxicheng.org/about/

    本博客的文章集合:

  • 相关阅读:
    [设计模式]之二:策略模式
    [设计模式]之一:简单工厂模式
    [iOS] WSHorizontalPickerView 图片水平滚动封装
    [闲谈] 有经验的程序员用Google用得多么?
    Flutter 布局(四)- Baseline、FractionallySizedBox、IntrinsicHeight、IntrinsicWidth详解
    Flutter 布局(二)- Padding、Align、Center详解
    Flutter 布局(一)- Container详解
    Flutter 布局详解
    Flutter Plugin开发流程
    现有项目中集成Flutter
  • 原文地址:https://www.cnblogs.com/xc1234/p/9166008.html
Copyright © 2020-2023  润新知