关于C语言中继承和多态的实现

以下的内容综合了多篇文章,加上一点自己的理解而成。目的为了给自己阅读他们文章后做一个笔记。在末尾给出了这些文章的地址。

    多态的实现可以采用以下几种方式:
    (1)使用 vod * (万能指针)来实现“编译时多态”。
    (2)使用函数指针来实现“运行时多态”。
    (3)使用型如struct struct_name{

              ...............................
              char temp[0]; //或者char *temp;
          };
这种形式。

对于(1)举例如下:

void HandleMsg(unsinged int id, void *p)
{
    Msg1 *p1;
    Msg2 *p2;

    switch(id)
    {
    case key1:
        p1 = (Msg1*)p;
        //do something
        break;
    case key2:
        p2 = (Msg2*)p;
        //do something
        break;
    default:
        break;
    }
}

    这个例子也许不能说明函数的编译时多态,因为没有使用函数指针与vod * 之间转换来转换去。没有举这类例子,是因为想避免这种用法。
    对于(2)举例如下:
#ifndef C_Class
       #define C_Class struct
#endif

C_Class A {

       C_Class A *A_this;
       void (*Foo)(C_Class A *A_this);
       int a;

       int b;

};

C_Class B{               //B继承了A

       C_Class B *B_this;  //顺序很重要
       void (*Foo)(C_Class B *Bthis); //虚函数
       int a;
       int b;
       int c;
}; 

void B_F2(C_Class B *Bthis)
{
       printf("It is B_Fun/n");
}

void A_Foo(C_Class A *Athis)
{

       printf("It is A.a=%d/n",Athis->a);//或者这里
}

void B_Foo(C_Class B *Bthis)
{
    printf("It is B.c=%d/n",Bthis->c);
}

void A_Creat(struct A* p)
{
   p->Foo=A_Foo;
   p->a=1;
   p->b=2;
   p->A_this=p;

void B_Creat(struct B* p)
{
   p->Foo=B_Foo;
   p->a=11;
   p->b=12;   
   p->c=13;
   p->B_this=p;

}

int main(int argc, char* argv[])
{

       C_Class A *ma,a;
       C_Class B *mb,b;

       A_Creat(&a);//实例化
       B_Creat(&b);

       mb=&b;
       ma=&a;

       ma=(C_Class A*)mb;//引入多态指针

       printf("%d/n",ma->a);//可惜的就是 函数变量没有private

       ma->Foo(ma);//多态
       a.Foo(&a);//不是多态了
       B_F2(&b);//成员函数,因为效率问题不使用函数指针
       return 0;
}


    在C中实现继承。
    对于例(1)中,有个重大的缺点,那就是缺乏类型安全。那么下面就可以使用继承来实现保证类型安全。

typedef struct tagT_MsgHeader {
    int id;
    //...
}MsgHeader;

typedef struct tagT_Msg1 {
    MsgHeader           h;
    int                 a;
    int                 b;
}Msg1;

typedef struct tagT_Msg2 {
    MsgHeader h;
    int       c;
    int       d;
}Msg2;

然后再重新定义消息处理函数:

void HandleMsg(MsgHeader *ph)
{
    Msg1 *p1;
    Msg2 *p2;

    switch(ph->id)
    {
    case key1:
        p1 = (Msg1*)(ph);
        //do something
        break;
    case key2:
        p2 = (Msg2*)ph;
        //do something
        break;
    default:
        break;
    }
}

通过继承保证了类型安全,但是为了保证继承后的结构体灵活性,继承的变量MsgHeader h不能作为第一个成员变量,那么p1 =

(Msg1*)(ph)这种强制转换就无法得到正确的p1的地址,不过通过定义一个宏来实现这个:

#define CONTAINING_RECORD(address, type, field) ((type *)( /
                                                  (PCHAR)(address) - /
                                                  (UINT_PTR)(&((type *)0)->field)))
    这个类似的宏定义在linux中最常见。这样传入的MsgHeader 的指针经过宏处理,无论MsgHeader h在结构体的那个位置,均能获得继承后的结构体的首地址,这样再来强制转换就没有问题了。

顺便解释一下这个宏:
    #define CONTAINING_RECORD(address, type, field) ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

    先看&((type *)0)->field,它把“0”强制转化为指针类型,则该指针指向“0”(数据段基址)。因为指针是“type *”型的,所以可取到以“0”为基地址的一个type型变量field域的地址,这个地址也就等于field域到结构体基地址的偏移字节数。当前地址减去偏移地址便得出该结构体的地址。转换为(type *)型的指针。


    在c中实现纯虚类,可以通过在结构体使用函数指针成员来实现。

//------------------结构体中的函数指针类似于声明子类中必须实现的虚函数-------------
typedef struct
 {
  void  (*Foo1)();
  char  (*Foo2)();
  char*  (*Foo3)(char* st);
 }MyVirtualInterface;
//---------------------------------------------------------------------------------

//------------------类似于纯虚类的定义---------------------------------------------
 MyVirtualInterface* m_pInterface;
 
 DoMyAct_SetInterface(MyVirtualInterface* pInterface)
 {
  m_pInterface= pInterface;
 }

 void oMyAct_Do()
 {
  if(m_pInterface == NULL) return;
  m_pInterface->Foo1();
  c = m_pInterface->Foo2();
 }
//---------------------------------------------------------------------------------

//--------------------------子类一-------------------------------------------------
MyVirtualInterface  st[MAX];

//接着定义一些需要实现的函数 Act1_Foo1,Act1_Foo2,Act1_Foo3

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];
}
//-----------------------------------------------------------------------------------

//--------------------------主函数---------------------------------------------------
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
    }
}
//-----------------------------------------------------------------------------------

如果父类不为纯虚类的类,那么在子类中通过宏来实现虚函数

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_DoByOther() {}   //当然就可以添加新的实现

引用:

[1]http://blog.csdn.net/baoxingbo/articles/56406.aspx

[2]http://hi.baidu.com/blue_never_died/blog/item/b05f242d389bb734349bf7dd.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/452409.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

边界测试

边界条件边界测试是单元测试中最后的也可能是最重要的任务。 软件常常在它的边界上失效,例如,处理n元数组的第n个元素时,或做到i次循环中的第i次重复时,往往会发生错误。 使用刚好小于、刚好等于和刚好大于最大值或最小值的数据结…

阿里云上Kubernetes集群联邦

摘要: kubernetes集群让您能够方便的部署管理运维容器化的应用。但是实际情况中经常遇到的一些问题,就是单个集群通常无法跨单个云厂商的多个Region,更不用说支持跨跨域不同的云厂商。这样会给企业带来一些担忧,如何应对可用区级别…

缺氧游戏计算机,缺氧PC最低什么配置一览 你觉得高吗

缺氧PC最低什么配置一览,你觉得高吗。游戏对于电脑有不同程度的要求,缺氧这款游戏也有着自己的配置要求,看看下面的缺氧PC最低什么配置一览,你的硬件够得上吗。缺氧最低配置:首先公布的是官 方配置需求,目前…

Diango博客--14.使用 Django 项目中的 ORM 编写伪造测试数据脚本

文章目录0.思路引导1.脚本目录结构2.使用 Faker 快速生成测试数据3.批量生成测试数据4.执行脚本5.效果展示0.思路引导 1)为了防止博客首页展示的文章过多以及提升加载速度,可以对文章列表进行分页展示。 2)不过这需要比较多的文章才能达到分…

基于Sql Server 2008的分布式数据库的实践

配置Sql Server 2008(Win7) 1.打开SQL server2012,使用windows身份登录 2.登录后,右键选择“属性”。左侧选择“安全性”,选中右侧的“SQL Server 和 Windows 身份验证模式”以启用混合登录模式 3.选择“连接”&#x…

横向技术分析C#、C++和Java优劣

本文将从技术人员的角度横向分析C#、C和Java优劣,其实选择Java阵营还是.NET阵营,大家可以根据自己的实际需要来确定。 C#诞生之日起,关于C#与Java之间的论战便此起彼伏,至今不辍。抛却Microsoft与Sun之间的恩怨与口角,…

软件测试中的存根程序

存根程序用来代替被测试的模块所调用的模块,因此存根程序也称为“虚拟子程序”,它利用被它代替的模块的接口,只做尽可能少的数据操作。

计算机网络应用云计算,计算机网络云计算的类型

原标题:计算机网络云计算的类型随着现代计算机网络技术的不断发展,越来越多的与计算机网络有关的现代化技术得以出现,并且有着广泛的应用,其中云计算技术就是比较常见的一种,在实际应用中发挥着较高的价值。在信息时代…

sublime_text快捷键

1、注释:选中文本后,CTRL / 2、CTRL N,CTRLS,保存成.html文件后,只需要输入感叹号!,然后tab键,即可打印出基本的html格式!转载于:https://www.cnblogs.com/JAVA-STUDYER/p/855040…

Diango博客--15.通过 Django Pagination 实现简单分页(一)

文章目录0.思路引导1.Paginator 类的常用方法2.用 Paginator 给文章列表分页3.在模板中设置分页导航4.效果展示0.思路引导 1)当博客上发布的文章越来越多时,通常需要进行分页显示,以免所有的文章都堆积在一个页面,影响用户体验。…

SpringMVC 测试 mockMVC

SpringMVC测试框架 基于RESTful风格的SpringMVC的测试,我们可以测试完整的Spring MVC流程,即从URL请求到控制器处理,再到视图渲染都可以测试。 一 MockMvcBuilder MockMvcBuilder是用来构造MockMvc的构造器,其主要有两个实现&…

自顶向下和自底向上测试的优缺点

自顶向下测试方法的主要优点是不需要测试驱动程序,能够在测试阶段的早期实现并验证系统的主要功能,而且能在早期发现上层模块的接口错误。 自顶向下测试方法的主要缺点是需要存根程序,可能遇到与此相联系的测试困难,低层关键模块中…

C++ class中的静态(static)成员

C class中的静态(static)成员 (1) 静态数据成员 ①一般地静态数据成员在该类定义之外被初始化,如同一个成员函数被定义在类定义之外一样。在这种定义中的静态成员的名字必须被其类名限定修饰,例如下面是_interestRate的初始…

用计算机弹可惜不是你,可惜不是你 还是幸亏不是你

一、 你没有再挽留 我也没有再回头 就这样 无风无雨也无晴 无疾而终二、 是我孤陋寡闻不知你心有人三、 如果作业有葬礼,全体学生定当盛装出席.四、 纵使我有千般好 你也看不到 因为你没有一双爱我的眼睛五、 原来暂时共你没缘分 来年先会变得更合衬六、 真的别回头 你有未来 你…

PHP 完整实战23种设计模式

PHP实战创建型模式 单例模式 工厂模式 抽象工厂模式 原型模式 建造者模式 PHP实战结构型模式 桥接模式 享元模式 外观模式 适配器模式 装饰器模式 组合模式 代理模式 过滤器模式 PHP实战行为型模式 模板模式 策略模式 状态模式 观察者模式 责任链模式 访问者模…

Diango博客--16.稳定易用的 Django 分页库,完善分页功能(二)

文章目录0.思路引导1.分页效果概述2.分页思路3.Django 第三方拓展:django-pure-pagination4.自定义模板0.思路引导 1)在前面我们通过 Django Pagination 实现简单分页 中,我们实现了一个简单的分页导航。但效果有点差强人意,我们…

回归测试

在集成测试过程中,每当一个新模块结合进来时,程序就发生了变化:建立了新的数据流路径,可能出现了新的I/O操作,激活了新的控制逻辑。在集成测试的范畴中,回归测试是指重新执行已经做过的测试的某个子集&…

不同的写法 其中 1 2 (试了下 没有效果 ,先记载这里把)

转载于:https://www.cnblogs.com/kaibindirver/p/9145455.html

美国西北大学 计算机工程专业排名,[转载]美国西北大学计算机工程研究生最新专业排名...

对于打算去美国西北大学读研究生的学生来讲,美国西北大学研究生申请要求及美国西北大学研究生专业介绍是学生最关心的问题。本文香港介绍美国西北大学研究生申请要求及美国西北大学研究生的专业介绍,帮助更多的学生更好的了解美国西北大学。2016年西北大…

析构函数virtual与非virtual区别

作为通常的原则,如果一个类定义了虚函数,那么它的析构函数就应当是virtual的。因为定义了虚函数则隐含着:这个类会被继承,并且会通过基类的指针指向子类对象,从而得到多态性。 这个类可能会被继承,并且会…