C语言中的面向对象(2)-C语言的多态实现

2016-02-19 20:21 4 1 收藏

下面图老师小编跟大家分享一个简单易学的C语言中的面向对象(2)-C语言的多态实现教程,get新技能是需要行动的,喜欢的朋友赶紧收藏起来学习下吧!

【 tulaoshi.com - 编程语言 】

相信很多人都看过设计模式方面的书,大家有什么体会呢?Bridge,Proxy,Factory这些设计模式都是基于抽象类的。使用抽象对象是这里的一个核心。
        
   !-- frame contents -- !-- /frame contents --   其实我觉得框架化编程的一个核心问题是抽象,用抽象的对象构建程序的主体框架,这是面向对象编程的普遍思想。用抽象构建骨架,再加上多态就形成了一个完整的程序。由于C++语言本身实现了继续和多态,使用这样的编程理念(理念啥意思?跟个风,嘿嘿)在C++中是十分普遍的现象,可以说Virtual(多态)是VC的灵魂。
   
  但是,使用C语言的我们都快把这个多态忘光光了。我常听见前辈说,类?多态?我们用的是C,把这些忘了吧。很不幸的是,我是一个固执的人。这么好的东西,为啥不用呢。很兴奋的,在最近的一些纯C代码中,我看见了C中的多态!下面且听我慢慢道来。
   
  1. VC中的Interface是什么
  
  Interface:中文解释是接口,其实它表示的是一个纯虚类。不过我所要说的是,在VC中的Interface其实就是strUCt,查找Interface的定义,你可以发现有这样的宏定义:
  
         #Ifndef Interface
         #define Interface struct
         #endif
  
  而且,实际上在VC中,假如一个类有Virtual的函数,则类里面会有vtable,它实际上是一个虚函数列表。实际上C++是从C发展而来的,它不过是在语言级别上支持了很多新功能,在C语言中,我们也可以使用这样的功能,前提是我们不得不自己实现。
   
  2.C中如何实现纯虚类(我称它为纯虚结构)
  
  比较前面,相信大家已经豁然开朗了。使用struct组合函数指针就可以实现纯虚类。
  
  例子:     typedef struct {
          void  (*Foo1)();
          char  (*Foo2)();
          char*  (*Foo3)(char* st);
      }
      MyVirtualInterface;
        
  这样假设我们在主体框架中要使用桥模式。(我们的主类是DoMyAct,接口具体实现类是Act1,Act2)下面我将依次介绍这些“类”。(C中的“类”在前面有说明,这里换了一个,是使用早期的数组的办法)
   
  主类DoMyAct: 主类中含有MyVirtualInterface* m_pInterface; 主类有下函数:
  
      DoMyAct_SetInterface(MyVirtualInterface* pInterface)
      {
          m_pInterface= pInterface;
      }
      DoMyAct_Do()
      {
          if(m_pInterface==NULL) return;
          m_pInterface-Foo1();
          c=m_pInterface-Foo2();
      }
  
  子类Act1:实现虚结构,含有MyVirtualInterface  st[MAX]; 有以下函数:
  
      MyVirtualInterface* Act1_CreatInterface()
      {
          index=FindValid() //对象池或者使用Malloc !应该留在外面申请,实例化
          if(index==-1) return NULL;
          St[index].Foo1=Act1_Foo1; // Act1_Foo1要在下面具体实现
  
           St[index].Foo2=Act1_Foo2;
          St[index].Foo3=Act1_Foo3;
          Return &st [index];
      }
  
  子类Act2同上。
   
  在main中,假设有一个对象List。List中存贮的是MyVirtualInterface指针,则有:
  
      if( (p= Act1_CreatInterface()) != NULL)
      List_AddObject(&List, p); //Add All
   
      While(p=List_GetObject()){
          DoMyAct_SetInterface(p);//使用Interface代替了原来大篇幅的Switch Case
          DoMyAct_Do();//不要理会具体的什么样的动作,just do it
      }
   
      FREE ALL 更多内容请看C/C++进阶技术文档专题,或    在微系统里面,比如嵌入式,通常使用对象池的技术,这个时候可以不用考虑释放的问题(对象池预先没有空间,使用Attach,在某个函数中申请一个数组并临时为对象池分配空间,这样函数结束,对象池就释放了)
   
   !-- frame contents -- !-- /frame contents --   但是在Pc环境下,由于程序规模比较大,更重要的是一些非凡的要求,使得对象的生命周期必须延续到申请的那个函数体以外,就不得不使用malloc,实际上即使在C++中,new对象的自动释放始终是一个令人头疼的问题,新的标准引入了智能指针。但是就我个人而言,我觉得将内存释放的问题完全的交给机器是不可信任的,它只能达到准最佳。
   
  你知道设计Java的垃圾回收算法有多困难吗?现实世界是错综复杂的,在没有先验条件下,要想得到精确的结果及其困难。所以我说程序员要时刻将free记在心上,有关程序的健壮性和自我防御将在另外一篇文章中讲述。
   
  3.纯虚结构的退化
  

  下面我们来看看假如struct里面仅仅有一个函数是什么? 这个时候假如我们不使用struct,仅仅使用函数指针又是什么? 我们发现,这样就退化为普通的函数指针的使用了。
   
  所以说,有的时候我觉得面向对象仅仅是一种形式,而不是一种技术。是一种观点,而不是一种算法。但是,正如炭,石墨和钻石的关系一样,虽然分子式都是C,但是组成方法不一样,表现就完全不一样了!
  
  有的时候,我们经常被编程中琐碎的事情所烦恼,而偏离了重心,其实程序可进化的特性是很重要的。有可能,第一次是不成功的,但是只要可进化,就可以发展。
   
  4.进阶――类结构树,父类不是纯虚类的类
  
  前面仅仅讲的是父类是纯虚结构的情况 (面向对象建议的是所有类的基类都是从纯虚类开始的), 那么当类层次比较多的情况下,出现父类不是纯虚结构怎么办呢。嘿嘿,其实在C中的实现比C++要简单多了。因为C中各个函数是分散的。
   
  在这里使用宏定义是一个很好的办法:比如两个类Act1,ActByOther1“继续”Act1:
  
      MyVirtualInterface* ActByOther1_CreatInterface()
      {
          index=FindValid() //对象池或者使用Malloc
          if(index==-1) return NULL;
          St[index].Foo1= ActByOther1_Foo1; // Act1_Foo1要在下面具体实现
          St[index].Foo2= ActByOther1_Foo2;
          St[index].Foo3= ActByOther1_Foo3;
          Return &st [index];
  
       }
   
      #define ActByOther1_Foo1 Act1_Foo1  //这就是继续 嘿嘿
      ActByOther1_Foo2(){}                    //  可以修改其实现
      ActByOther1_DoByOther() {}         //当然就可以添加新的实现咯
   
  5.实例――可以参见H264的源码,其中NalTool就是这样的一个纯虚结构。 更多内容请看C/C++进阶技术文档专题,或

来源:http://www.tulaoshi.com/n/20160219/1623425.html

延伸阅读
基于对象的程序设计的好处大家应该都有一定的体会,但是在 C 语言中并不支持类的概念,不过我们可以通过 strUCt 实现一些基础的类对象,如队列、堆栈等。通过对象的实现可以在一定程度上提高编程效率、简化 C程序设计。下面使用一个例子来介绍下如何在实现一个简单的类,希望对大家有些益处: /* 应用消息队列类 */ struct Ap...
用位运算实现加法也就是计算机用二进制进行运算,32位的CPU只能表示32位内的数,这里先用1位数的加法来进行,在不考虑进位的基础上,如下 代码如下: 1 + 1 = 0 1 + 0 = 1 0 + 1 = 1 0 + 0 = 0 很明显这几个表达式可以用位运算的“^”来代替,如下 代码如下: 1 ^ 1 = 0 1 ^ 0 = 1 0 ^ 1 = 1 0 ^ 0 = 0 这样我们就完成了简单...
在C语言中,rand()函数可以用来产生随机数,但是这不是真真意义上的随机数,是一个伪随机数,是根据一个数,我们可以称它为种子,为基准以某个递推公式推算出来的一系数,当这系列数很大的时候,就符合正态公布,从而相当于产生了随机数,但这不是真正的随机数,当计算机正常开机后,这个种子的值是定了的,除非你破坏了系统,为了改变这个种子...
#pragma#pragma 预处理指令详解 在所有的预处理指令中,#Pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与C和 C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。 ...
1. exit 用于在程序运行的过程中随时结束程序,exit 的参数是返回给OS的。main函数结束时也会隐式地调用exit函数。exit函数运行时首先会执行由atexit()函数登记的函数,然后会做一些自身的清理工作,同时刷新所有输出流、关闭所有打开的流并且关闭通过标准I/O函数tmpfile()创建的临时文件。exit是结束一个进程,它将删除进程使用的内存空间,同...

经验教程

940

收藏

95
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部