C语言的对象化模型
面向对象的特征主要包括:
.封装,隐藏内部实现
.继承,复用现有代码
.多态,改写对象行为
采用C语言实现的关键是如何运用C语言本身的特性来实现上述面向对象的特征。

1.1  封装
封装是一种信息隐蔽技术,它体现于类的说明,是对象的重要特性。封装使数据和加工该数据的
方法(函数)封装为一个整体,以实现独立性很强的模块,使得用户只能见到对象的外特性(对象
能接受哪些消息,具有那些处理能力),而对象的内特性(保存内部状态的私有数据和实现加工能
力的算法)对用户是隐蔽的。封装的目的在于把对象的设计者和对象者的使用分开,使用者不必
知晓行为实现的细节,只须用设计者提供的消息来访问该对象。
在C语言中,大多数函数的命名方式是动词+名词的形式,例如要获取一个semaphore,会命名
成take_semaphore,重点在take这个动作上。面向对象编程中刚好相反,命名为rt_sem_take,即名词+动词的形式,重点在名词上,体现了一个对象的方法。另外对于某些方法,仅局限在对象内部使用,它们将采用static修辞把作用范围局限在一个文件的内部。通过这样的方式,把一些不想让用户知道的信息屏蔽在封装里,用户只看到了外层的接口,从而形成了面向对象中的最基本的对象封装实现。

一般属于某个类的对象会有一个统一的创建,析构过程。
.对象内存数据块已经存在,需要对它进行初始化 – rt_sem_init;
.对象内存数据块还未分配,需要创建并初始化 – rt_sem_create。
可以这么认为,对象的创建(create)是以对象的初始化(init)为基础的,创建动作相比较而言多了个
内存分配的动作。
相对应的两类析构方式:
.由rt_sem_init初始化的semaphore对象 – rt_sem_detach;
.由rt_sem_create创建的semaphore对象 – rt_sem_delete.

1.2  继承
继承性是子类自动共享父类之间数据和方法的机制。它由类的派生功能体现。一个类直接继承其
它类的全部描述,同时可修改和扩充。继承具有传递性。继承分为单继承(一个子类只有一父类)
和多重继承(一个类有多个父类,当前RT-Thread的对象系统不能支持)。类的对象是各自封闭的,
如果没继承性机制,则类对象中数据、方法就会出现大量重复。继承不仅支持系统的可重用性,而
且还促进系统的可扩充性。

类似的实现代码如下程序清单:
/*  父类  */
struct  parent_class
{
  int  a,  b;
  char  *str;
};

/*  继承于父类的子类  */
struct  child_class
{
  struct  parent class  p;
  int  a,  b;
};

/*  操作示例函数*/
void  func()
{
  struct  child_class  obj,  *obj_ptr;  /*  子类对象及指针  */
  struct  parent_class  *parent_ptr;  /*  父类指针  */
  obj_ptr  =  &obj;

  /*  取父指针  */
  parent_ptr  =  (struct  parent*)  &obj;

  /*  可通过转换过类型的父类指针访问相应的属性  */
  parent ptr->a  =  ;
  parent ptr->b  =  ;

  /*  子类属性的操作  */
  obj ptr->a  =  ;
  obj ptr->b  =  ;
}

在上面代码中,注意child_class结构中第一个成员p,这种声明方式代表child_class类型的数据中
开始的位置包含一个parent_class类型的变量。在函数func中obj是一个child_class对象,正像这个
结构类型指示的,它前面的数据应该包含一个parent_class类型的数据。在第21行的强制类型赋值
中parent_ptr指向了obj变量的首地址,也就是obj变量中的p对象。好了,现在parent_ptr指向的是
一个真真实实的parent类型的结构,那么可以按照parent的方式访问其中的成员,当然也包括可以
使用和parent结构相关的函数来处理内部数据,因为一个正常的,正确的代码,它是不会越界访
问parent结构体以外的数据。
经过这基本的结构体层层相套包含,对象简单的继存关系就体现出来了:父对象放于数据块的最
前方,代码中可以通过强制类型转换获得父对象指针。

1.3  多态
    对象根据所接收的消息而做出动作。同一消息为不同的对象接受时可产生完全不同的行动,这种
现象称为多态性。利用多态性用户可发送一个通用的信息,而将所有的实现细节都留给接受消息
的对象自行决定,如是,同一消息即可调用不同的方法。例如:抽象设备具备接口统一的读写接口。
串口是设备的一种,也应支持设备的读写。但串口的读写操作是串口所特有的,不应和其他设备
操作完全相同,例如操作串口的操作不应应用于SD卡设备中。
    多态性的实现受到继承性的支持,利用类继承的层次关系,把具有通用功能的协议存放在类层次
中尽可能高的地方,而将实现这一功能的不同方法置于较低层次,这样,在这些低层次上生成的
对象就能给通用消息以不同的响应。

对象模型采用结构封装中使用指针的形式达到面向对象中多态的效果,例如:
/*  抽象父类  */
struct  parent_class
{
  int  a;

  /*  反映不同类别属性的方法  */
  void  (*vfunc)(int  a);
}

/*  抽象类的方法调用  */
void  parent_class_vfunc(struct  parent_class  *self,  int  a)
{
  assert(self  !=  NULL);
  assert(slef->vfunc  !=  NULL);

  /*  调用对象本身的虚拟函数  */
  self->vfunc(a);
}

/*  继承自parent class的子类  */
struct  child_class
{
  struct  parent_class  parent;
  int  b;
};

/*  子类的构造函数  */
void  child_class_init(struct  child_class*  self)
{
  struct  parent_class*  parent;

  /*  强制类型转换获得父类指针  */
  parent  =  (struct  base_class*)  self;
  assert(parent  !=  NULL);

  /*  设置子类的虚拟函数  */
  parent->vfunc  =  child_class_vfunc;
}

/*  子类的虚拟函数实现  */
static  void   child class vfunc(struct  child class*self,  int  a)
{
  self->b  =  a  +  ;
}

阅读()| 评论()

喜欢推荐转载
 MIDI数据完成实现标准的DTMF信号 海量数据处理方法
最近读者
登录后,您可以在此留下足迹。 liu_jing07
liu_jing
  zhansh21cn
zhansh21
  zougang_215
zougang_
  liuxueuestc
liuxueue
  shuangyu226@
shuangyu
  dmlgt123
dmlgt123
 张俊勇
张俊勇
  xiaoying1277
xiaoying
热度

  yzfcer
yzfcer

关闭
玩LOFTER,免费冲印20张照片,人人有奖!     我要抢>
评论
点击登录|昵称:

江苏 泰州
- :
songyajun0107
太精彩了!人才啊!受益匪浅啊
回复
广东 佛山
-- :
双木
我水平还没到,咋办
回复

-- :
woshizhaozigeng
我仿照您的思路写了一个简单的多态例子,不过实现的不好看, 基类里的虚函数参数void  (*vfunc)(struct parent_class *self, int  a);   /包含一个基类类型指针.
下面每个派生类自己的虚函数实现void  (*vfunc)(struct parent_class *self, int  a);   也要包含一个基类的指针.这样写我感觉很难看..
回复

-- :
woshizhaozigeng
struct  parent_class
{
 int  a;
 void  (*vfunc)(struct parent_class *self, int  a);   //虚函数
};
struct child_class
{
 struct parent_class base;
 void (vfunc_my_implemention)(struct parent_class *self, int a);
};

void child_vfunc(struct parent_class *self, int info)
{
 printf("这是子类实现的虚方法........\n");
}
void init_child_class(struct child_class *self)
{
 ((struct parent_class *)self)->vfunc = child_vfunc;
}
int main()
{
 struct parent_class *ptrparent_class = NULL;
 struct child_class c_object;
 init_child_class(&c_object);
 ptrparent_class = (struct parent_class *)&c_object;
 ptrparent_class->vfunc(ptrparent_class, );                 //多态
 ;
}
回复

-- :
woshizhaozigeng
你好,您的关于c语言实现实现多态的部分代码实现有问题。。
当用c实现封装里,由于没有编译器的支持,所以每个函数参数要添加一个指向自己的指针struct  child class*self,相当于c++里面的pthis,所以您说的抽象父类的虚函数里面也要添加一个pthis指针,而且要参数要是void*类型的。您说对吗?
/*  抽象父类  */
struct  parent_class
{
  int  a;

  /*  反映不同类别属性的方法  */
  void  (*vfunc)(int  a);         //改正一下. void(*vfunc)(void *self, int a);
}
     Redy的开发语言是C,但在源码中,有很多地方都使用到了面向对象编程的方法,例如:在基本数据类型这一个模块,所有的数据类型都继承robject;在抽象语法树模块,所有的节点都继承astobjct。在linux内核中,也有很多是使用的面向对象方法,在虚拟文件系统,驱动模型中都可以看到。c语言是一种结构化编程语言,以模块工能和处理过程设计为主,实现数据与代码分隔化。面向对象方法论中,核心是类,类是用于创造对象的模板,其三要素为:封装,继承,多态。C语言本身对面向对象的支持很弱,但可以通过一些技巧来实现。下面通过一个具体的实例来说明实现这此技巧。

实例介简:

       在几何中,所有的几何类型都继承父类“形状(shape)”,父类“形状”有两处属性s_type和s_name。其中s_type用于表示该形状所属的类型,s_name用于表于该形状态的名称。而且父类shape还有两个虚接口,一个为shape_area用于返回该形状的面积,一个为shape_perimeter用于返回该形状的周长。所子继承“形状”的子类都必须实现这两个接口。
struct shape;
struct shape_ops
{
    /*返回几何体的面积*/
    float (*so_area)(struct shape*);
    /*返回几何体的周长*/
    int (*so_perimeter)(struct shape*);
};
struct shape
{
    int* s_type;
    char* s_name;
    struct shape_ops* s_ops; /*虚接口,所有子类必须实现*/
};

float shape_area(struct shape* s)  /*求形状面积*/
{
    return s->s_ops->so_area(s);
}
int shape_perimeter(struct shape* s) /*求周长*/
{
    return s->s_ops->so_perimeter(s);
}
        几何体“三角形(triangle)”继承父类“形状”,并且实现了父类的两个虚接口。“三角形”有三条边,分别用t_side_a,t_side_b,t_side_c来表于三条边的长度。
/*三角形*/
struct triangle
{
    struct shape t_base;
    int t_side_a;
    int t_side_b;
    int t_side_c;
};

float triangle_area(struct shape* s)  /*三角形面积,用海伦公式*/
{
    struct triangle* t=(struct triangle*)s;
    int a=t->t_side_a;
    int b=t->t_side_b;
    int c=t->t_side_c;
    ;
    return sqrt(p*(p-a)*(p-b)*(p-c));
}
int triangle_perimeter(struct shape* s)  /*三角形周长*/
{
    struct triangle* t=(struct triangle*)s;
    int a=t->t_side_a;
    int b=t->t_side_b;
    int c=t->t_side_c;
    return a+b+c;
}
struct shape_ops triangle_ops=    /*对父类虚接口的实现*/
{
    triangle_area,
    triangle_perimeter,
};
struct triangle* triangle_create(int a,int b,int c)  /*创建三角形*/
{
    struct triangle* ret=(struct triangle*)malloc(sizeof (*ret));
    ret->t_base.s_name="triangle";
    ret->t_base.s_ops=&triangle_ops;
    ret->t_side_a=a;
    ret->t_side_b=b;
    ret->t_side_c=c;
    return ret;
}
        几何体“矩形(rectangle)”继承父类“形状”,同样也实现的父类的两个虚接口。有两个属性r_width和r_height,分别表示矩形的长和宽。
/*矩形*/
struct rectangle
{
    struct shape r_base;
    int r_width;
    int r_height;
};

float rectangle_area(struct shape* s)  /*矩形面积*/
{
    struct rectangle* r=(struct rectangle*)s;
    return r->r_width*r->r_height;
}
int rectangle_perimeter(struct shape* s)/*矩形周长*/
{
    struct rectangle* r=(struct rectangle*)s;
    ;
}
struct shape_ops rectangle_ops=      /*对父类虚接口的实现*/
{
    rectangle_area,
    rectangle_perimeter,
};

struct rectangle* rectangle_create(int width, int height)  /*创建矩形*/
{
    struct rectangle* ret=(struct rectangle*)malloc(sizeof(*ret));
    ret->r_base.s_name="rectangle";
    ret->r_base.s_ops=&rectangle_ops;
    ret->r_height=height;
    ret->r_width=width;
    return ret;
}
测试代码:
int main()
{
    ];
    s[]=triangle_create(,,);
    s[]=triangle_create(,,);
    s[]=rectangle_create(,);
    s[]=rectangle_create(,);

    ;
    ;i<;i++)
    {
        float area=shape_area(s[i]);
        int perimeter=shape_perimeter(s[i]);
        char* name=s[i]->s_name;

        printf("name:%s ,area:%.2f ,perimeter:%d\n",name,area,perimeter);
    }
    ;
}

运行结果:
name:triangle ,area:
name:triangle ,area:
name:rectangle ,area:
name:rectangle ,area:
我谈C语言和面向对象 by 招

之前看完骏豪写的《C语言实现面向对象编程》,同时发现最近blog没有高质量的编程系文章orz,于是就一直想写篇东西谈谈语言和设计,,也算是对自己做程序和OOAD的经验教训小结
打好草稿之后发现又长又臭,索性换一种短而快的方式——分节,一来好写,二来免得大家懒得看=_,=
PS:貌似我还欠着n篇游戏特效制作的文章。。。囧

首先更正骏豪的几个错误观点:P
.面向对象和基于对象是两个不完全相同的东西。
基于对象就是类最基本的特征,将数据和方法绑定到一起。
在C这样没有类的语言中可以用一个结构表示一个类的数据,然后在所有与之相关的函数(方法)中传入这个结构(或指针),也能达到同样的效果,典型例子如C标准库的FILE
另外Win32API中采取的是句柄,即用一个数值作为对象的ID,去代替你操作的对象,同样能达到封装数据的目的。
但是这并未达到面向对象的程度

面向对象是在基于对象的基础上做到继承和多态,尤其是多态,这是与基于对象的本质区别,也是面向对象与结构化编程最不同的地方
C可以实现多态,方法是定义一个struct,包含若干函数指针,然后由函数指针指向具体的函数以实现多态
然而要真正实现多态,那么需要衍生出很多“周边产品”——运行时类型检查(RTTI,例如C#的is关键字)、反射等等。这点要在C语言实现只能用取巧的办法,例如自动分配类的id然后查id来匹配,或者用宏等等。
具体可以参考GTK,它的编写风格是面向对象,也支持RTTI。

所以Windows至少从API的设计看来并不是面向对象。

.C++ is better than C? OO is better than structral programming?
没有面向对象,甚至没有基于对象,我们真的写不出一个大型的程序吗?
我想大部分初学C++的人都会接受这么一种观点:C++是更好的C语言(很不幸连张锋也这么跟我们说。。囧),至少你爱用OO的特性就用,不爱就当它是C来用。
类似地,面向对象(OO)才是王道,结构化编程纯属老土——这样的观点也充斥于很多C++新手甚至是有相当经验的人士当中。
我写这段并非想争论C和C++孰优孰劣。理解如何通过C语言进行高质量编程对程序设计有非常大的帮助,包括面向对象的能力。

我先把问题放这:C语言+结构化编程能写高质量的大型程序。问题是怎么写?

用C语言实现面向对象的开发的更多相关文章

  1. OO开发思想:面向对象的开发方法(Object oriented,OO)

    面向对象的开发方法(Object oriented,OO)认为是好文章吧,拿来分享一下(转载) 面向对象的开发方法(Object oriented,OO) 从事软件开发的工程 师们常常有这样 的体会: ...

  2. 基于C语言的面向对象编程

    嵌入式软件开发中,虽然很多的开发工具已经支持C++的开发,但是因为有时考虑运行效率和编程习惯,还是有很多人喜欢用C来开发嵌入式软件.Miro Samek说:"我在开发现场发现,很多嵌入式软件 ...

  3. 已看1.熟练的使用Java语言进行面向对象程序设计,有良好的编程习惯,熟悉常用的Java API,包括集合框架、多线程(并发编程)、I/O(NIO)、Socket、JDBC、XML、反射等。[泛型]\

    1.熟练的使用Java语言进行面向对象程序设计,有良好的编程习惯,熟悉常用的Java API,包括集合框架.多线程(并发编程).I/O(NIO).Socket.JDBC.XML.反射等.[泛型]\1* ...

  4. 使C语言实现面向对象的三个要素,你掌握了吗?

  5. 一步步分析:C语言如何面向对象编程

    这是道哥的第009篇原创 一.前言 在嵌入式开发中,C/C++语言是使用最普及的,在C++11版本之前,它们的语法是比较相似的,只不过C++提供了面向对象的编程方式. 虽然C++语言是从C语言发展而来 ...

  6. Python语言之面向对象

    Python语言之面向对象 前言 面向对象 -- Object Oriented 简写:OO 面向对象设计 -- Object Oriented Design 简写:OOD 面向对象编程 -- Obj ...

  7. 我的首个MOOC课程《面向对象软件开发实践》

    我的首个MOOC课程<面向对象软件开发实践> 我将在网易云课堂开讲MOOC课<面向对象软件开发实践>(http://mooc.study.163.com/course/YOOK ...

  8. [java学习笔记]java语言核心----面向对象之this关键字

    一.this关键字 体现:当成员变量和函数的局部变量重名时,可以使用this关键字来区别:在构造函数中调用其它构造函数 原理:         代表的是当前对象.         this就是所在函数 ...

  9. JS面向对象使用面向对象进行开发

      面向对象基础一之初体验使用面向对象进行开发 对 JS 中的面向对象的基础进行讲述, 初体验使用面向对象进行开发 主要内容是 面向对象的概念及特性 用面向对象的方式解决简单的标签创建实例 一些基础的 ...

随机推荐

  1. 想弄一弄tensorflow,先弄numpy

    现在晚上凉快点了, 下班回家可以学会东东了.. 这次的书是一个印度人写的. 按着示例代码弄起先.. #!/usr/bin/env python # -*- coding: utf-8 -*- impo ...

  2. print、println的区别

    System.out.print("a");——后面没有换行符 System.out.println("b");——后面有换行符 换行符:\r和\n以及\r\n ...

  3. VIM配置示例

    以下是我习惯的vim配置,做个记录~_~ " 文件编码 set fileencoding=utf- set encoding=utf- set termencoding=utf- " ...

  4. 《java虚拟机》----虚拟机字节码执行引擎

    No1: 物理机的执行引擎是直接建立在处理器.硬件.指令集合操作系统层面上的,而虚拟机的执行引擎则是由自己实现的,因此可以自行制定指令集与执行引擎的结构体系,并且能够执行那些不被硬件直接支持的指令集格 ...

  5. 洛谷P1528 切蛋糕 [搜索,二分答案]

    题目传送门 切蛋糕 题目描述 Facer今天买了n块蛋糕,不料被信息组中球球等好吃懒做的家伙发现了,没办法,只好浪费一点来填他们的嘴巴.他答应给每个人留一口,然后量了量每个人口的大小.Facer有把刀 ...

  6. mysql概念特性和优化

    概念特性 基础命令 连接 监控 优化 字段 索引 查询 共享锁(shared lock)和排它锁(exclusive lock) 也叫读锁(red lock)和写锁(write lock) 多版本并发 ...

  7. nyoj 211&&poj 3660

    Cow Contest 时间限制:1000 ms  |  内存限制:65535 KB 难度:4 描述 N (1 ≤ N ≤ 100) cows, conveniently numbered 1..N, ...

  8. PHP视频教程 字符串处理函数(三)

    字符串替换函数: str_replace() 替换字符串或数组元素,区分大小,第四个参数可选用于统计替换次数. str_ireplace() 不区分大小写替换 字符串函数比较 strcmp()比较字符 ...

  9. 范浩强treap——可持久化

    当平衡树需要可持久化的时候,意味着我们需要访问以前的某个时间点的平衡树,就要保持以前的树形态不变,新建一个时间戳,构建一棵新的树. 如果用以前的旋转treap可能就不方便做到(又要打时间戳,又要新建节 ...

  10. pat 素数对猜想

    让我们定义d​n​​为:d​n​​=p​n+1​​−p​n​​,其中p​i​​是第i个素数.显然有d​1​​=1,且对于n>1有d​n​​是偶数.“素数对猜想”认为“存在无穷多对相邻且差为2的素 ...