c中实现多态的例子
C语言中的多态

C语⾔中的多态⼀、多态的主要特点1、继承体系下。
继承:是⾯向对象最显著的⼀个特性。
继承是从已有的类中派⽣出新的类,新的类能吸收已有类的数据属性和⾏为,并能扩展新的能⼒,已有类被称为⽗类/基类,新增加的类被称作⼦类/派⽣类。
2、⼦类对⽗类的虚函数进⾏重写。
3、虚表。
在⾯向对象语⾔中,接⼝的多种不同现⽅式即为多态。
同⼀操作作⽤于不同的对象,可以有不同的解释,产⽣不同的执⾏结果,这就是多态性。
简单说就是允许基类的指针指向⼦类的对象。
⼆、代码实现1、C++中的继承与多态1 class Base2 {3 public:4 virtual void fun() {} //基类函数声明为虚函数5 int B1;6 };7 class Derived :public Base //Derived类公有继承Base类8 {9 public:10 virtual void fun() { //函数重写,此时基类函数可以声明为虚函数,也可以不声明11 cout << "D1.fun" << endl;12 }13 int D1;14 };15 int main(){16 Base b1; //创建⽗类对象17 Derived d1;//创建⼦类对象1819 Base *p1 = (Base *)&d1;//定义⼀个⽗类指针,并通过⽗类指针访问⼦类成员20 p1->fun();2122 Derived *p2 = dynamic_cast<Derived*> (&b1); //dynamic_cast⽤于将⼀个⽗类对象的指针转换为⼦类对象的指针或引⽤(动态转换)23 p2->fun();2425 getchar();26 return 0;27 }2. C语⾔实现C++的继承与多态1 typedef void(*FUNC)(); //定义⼀个函数指针来实现对成员函数的继承2 struct _Base //⽗类3 {4 FUNC _fun;//由于C语⾔中结构体不能包含函数,故借⽤函数指针在外⾯实现5 int _B1;6 };7 struct _Derived//⼦类8 {9 _Base _b1;//在⼦类中定义⼀个基类的对象即可实现对⽗类的继承10 int _D1;11 };12 void fb_() //⽗类的同名函数13 {14 printf("_b1:_fun()\n");15 }16 void fd_() //⼦类的同名函数17 {18 printf("_d1:_fun()\n");19 }20 int main() {21 _Base _b1;//定义⼀个⽗类对象_b122 _Derived _d1;定义⼀个⼦类对象_d12324 _b1._fun = fb_;//⽗类的对象调⽤⽗类的同名函数25 _d1._b1._fun = fd_;//⼦类的对象调⽤⼦类的同名函数2627 _Base *_p1 = &_b1;//定义⼀个⽗类指针指向⽗类的对象28 _p1-> _fun(); //调⽤⽗类的同名函数2930 _p1 = (_Base *)&_d1;//让⽗类指针指向⼦类的对象,由于类型不匹配所以要进⾏强转31 _p1->_fun(); //调⽤⼦类的同名函数3233 getchar();34 return 0;35 }。
C语言中的面向对象

C语言中的面向对象(1)-类模拟和多态,继承在面向对象的语言里面,出现了类的概念。
这是编程思想的一种进化。
所谓类:是对特定数据的特定操作的集合体。
所以说类包含了两个范畴:数据和操作。
而C语言中的struct仅仅是数据的集合。
(liyuming1978@)1.实例:下面先从一个小例子看起输出结果:11It is B.c=13It is A.a=1It is B_Fun2.类模拟解说:我在网上看见过一篇文章讲述了类似的思想(据说C++编程思想上有更加详细的解说,可惜我没空看这个了,如果有知道的人说一说吧)。
但是就象C++之父说的:“C++和C 是两种语言”。
所以不要被他们在语法上的类似就混淆使用,那样有可能会导致一些不可预料的事情发生。
其实我很同意这样的观点,本文的目的也不是想用C模拟C++,用一个语言去模拟另外一个语言是完全没有意义的。
我的目的是想解决C语言中,整体框架结构过于分散、以及数据和函数脱节的问题。
C语言的一大问题是结构松散,虽然现在好的大型程序都基本上按照一个功能一个文件的设计方式,但是无法做到更小的颗粒化――原因就在于它的数据和函数的脱节。
类和普通的函数集合的最大区别就在于这里。
类可以实例化,这样相同的函数就可以对应不同的实例化类的变量。
自然语言的一个特点是概括:比如说表。
可以说手表,钟表,秒表等等,这样的描述用面向对象的语言可以说是抽象(继承和多态)。
但是我们更要注意到,即使对应于手表这个种类,还是有表链的长度,表盘的颜色等等细节属性,这样细微的属性如果还用抽象,就无法避免类膨胀的问题。
所以说类用成员变量来描述这样的属性。
这样实例并初始化不同的类,就描述了不同属性的对象。
但是在C语言中,这样做是不可能的(至少语言本身不提供这样的功能)。
C语言中,如果各个函数要共享一个变量,必须使用全局变量(一个文件内)。
但是全局变量不能再次实例化了。
所以通常的办法是定义一个数组。
以往C语言在处理这样的问题的时候通常的办法就是这样,比如说socket的号,handel等等其实都是数组的下标。
C--程序设计--第10章-多态性及虚函数

使用重载函数注意:
不要使用重载函数描述不相干的函数 在类中,构造函数和普通成员函数均可以
重载 避免与函数的默认参数产生二义性
二、运算符重载
运算符重载(operate overloading)就是 赋予已有的运算符多重含义。
运算符重载实质是函数重载,运算符重载 的选择与函数重载类似,会根据运算符的 操作数的类型、个数和顺序来进行运算符 函数的选择。
#include<iostream.h> str#iinngc:l:usdter<isntgr(icnhga.rh>*s) v{}ossccsssc{s{{ittohtttolsstlsssls*drruarrrueptrepttepsi1trii3tc{pn=rin=rrn=pmn.<nn.<lprgncngncign=agp<*ggp<auitepgtepnte'irssrssbv\hwy:hwyghwnsit1ssitsla0=(:=(:=(tnr=ttnrit'scssscs:sc)rt1"rrt3scesss~ivci;thpt1hpsih1(.T23(.t::tttsnohn}ra,r.a,tza()gh(()grrrrttiatlrsilrsrer";eass;eiiiirdre[)ne[1i;[Ttt1ttnnnniglnl;gnl.nlhl)rlggggnep*e(e}(gesgeiei;2e(((gtrsnsnstnp(nsns)ncsi(lipg)gthg)ig(;(htn)en;t;tr;t;nti)a)artnthhih}ths<<ri{((;+n++<p<snd))}1g1s1aere*ige;]]i]nonszl{{;&;z;ddgd)&eercseelrl;s=teo1)m;a;/18etu)om/)0ut..;)构sr<""/;pn<造);//;s;/复}lp函构e<制n<数造ge构tn函hd造;l数};重} 载
C语言设计模式

目录1.C语言设计模式(开篇) (2)2.C语言和设计模式(继承、封装、多态) (3)2.1继承性 (3)2.2封装性 (4)2.3多态 (4)3.单件模式 (4)4.工厂模式 (5)5.抽象工厂模式 (6)6.访问者模式 (8)7.状态模式 (9)8.命令模式 (9)9.解释器模式 (10)10.备忘录模式 (11)11.观察者模式 (12)12.桥接模式 (12)13.建造者模式 (13)14.中介者模式 (14)15.策略模式 (15)16.适配器模式 (16)17.装饰模式 (17)18.亨元模式 (17)19.代理模式 (18)20.外观模式 (19)21.迭代器模式 (20)22.责任链模式 (21)23.模版模式 (22)24.组合模式 (24)25.原型模式 (25)1.C语言设计模式(开篇)关于软件设计方面的书很多,比如《重构》,比如《设计模式》。
至于软件开发方式,那就更多了,什么极限编程、精益方法、敏捷方法。
随着时间的推移,很多的方法又会被重新提出来。
其实,就我个人看来,不管什么方法都离不开人。
一个人写不出二叉树,你怎么让他写?敏捷吗?你写一行,我写一行。
还是迭代?写三行,删掉两行,再写三行。
项目的成功是偶然的,但是项目的失败却有很多原因,管理混乱、需求混乱、设计低劣、代码质量差、测试不到位等等。
就软件企业而言,没有比优秀的文化和出色的企业人才更重要的了。
从软件设计层面来说,一般来说主要包括三个方面:(1)软件的设计受众,是小孩子、老人、女性,还是专业人士等等;(2)软件的基本设计原则,以人为本、模块分离、层次清晰、简约至上、适用为先、抽象基本业务等等;(3)软件编写模式,比如装饰模式、责任链、单件模式等等。
从某种意义上说,设计思想构成了软件的主题。
软件原则是我们在开发中的必须遵循的准绳。
软件编写模式是开发过程中的重要经验总结。
灵活运用设计模式,一方面利于我们编写高质量的代码,另一方面也方便我们对代码进行维护。
C的运行时类型识别实现动态多态性

C的运行时类型识别实现动态多态性在C语言中,没有原生的运行时类型识别(Runtime Type Identification,RTTI)机制,而RTTI是实现多态性的关键。
然而,我们可以通过一些技巧和约定来模拟实现动态多态性,即在运行时根据对象的类型来决定调用哪个函数。
本文将介绍一种常用的C语言中实现动态多态性的方法。
一、使用函数指针表进行类型识别为了实现运行时类型识别,我们可以使用函数指针表(Function Pointer Table)来存储不同类型对象的函数指针。
函数指针表是一个包含一组函数指针的数组,数组的索引对应于对象的类型。
首先,我们定义一个基础的类型,作为其他类型的父类型,例如Shape类型:```ctypedef struct {void (*draw)();} Shape;```接下来,我们定义继承自Shape的具体类型,例如Rectangle和Circle:```ctypedef struct {Shape shape;int width;int height;} Rectangle;typedef struct {Shape shape;int radius;} Circle;```我们为每个具体类型实现相应的draw函数:```cvoid rectangle_draw() {printf("Drawing rectangle\n");}void circle_draw() {printf("Drawing circle\n");}```然后,我们为每个具体类型创建函数指针表,并将draw函数指针赋值给相应的表项:```cShape shape;shape.draw = rectangle_draw;Shape shape;shape.draw = circle_draw;```现在,我们可以通过调用shape.draw()来动态地调用相应类型的draw函数。
C语言设计模式

C++有三个最重要的特点,即继承、封装、多态。
我发现其实C语言也是可以面向对象的,也是可以应用设计模式的,关键就在于如何实现面向对象语言的三个重要属性。
(1)继承性[cpp]view plaincopy1.typedef struct _parent2.{3.int data_parent;4.5.}Parent;6.7.typedef struct _Child8.{9.struct _parent parent;10.int data_child;11.12.}Child;在设计C语言继承性的时候,我们需要做的就是把基础数据放在继承的结构的首位置即可。
这样,不管是数据的访问、数据的强转、数据的访问都不会有什么问题。
(2)封装性[cpp]view plaincopy1.struct _Data;2.3.typedef void (*process)(struct _Data* pData);4.5.typedef struct _Data6.{7.int value;8. process pProcess;9.10.}Data;封装性的意义在于,函数和数据是绑在一起的,数据和数据是绑在一起的。
这样,我们就可以通过简单的一个结构指针访问到所有的数据,遍历所有的函数。
封装性,这是类拥有的属性,当然也是数据结构体拥有的属性。
(3)多态[cpp]view plaincopy1.typedef struct _Play2.{3.void* pData;4.void (*start_play)(struct _Play* pPlay);5.}Play;多态,就是说用同一的接口代码处理不同的数据。
比如说,这里的Play结构就是一个通用的数据结构,我们也不清楚pData是什么数据,start_play是什么处理函数?但是,我们处理的时候只要调用pPlay->start_play(pPlay)就可以了。
剩下来的事情我们不需要管,因为不同的接口会有不同的函数去处理,我们只要学会调用就可以了。
C语言中的多态实现方式

C语言中的多态实现方式
多态是面向对象编程中一个重要的概念,它允许不同的对象对同一个消息做出不同的响应。
在C语言中,虽然没有内置的多态特性,但我们可以通过一些技巧来实现多态效果。
一种常见的实现多态的方式是使用函数指针。
函数指针可以指向不同的函数,我们可以将函数指针作为参数传递给一个函数,然后根据不同的函数指针调用不同的函数。
这样就可以实现在运行时根据对象类型来选择不同的函数处理。
另一种实现多态的方式是使用结构体和函数指针的组合。
我们可以定义一个结构体,其中包含一个函数指针指向操作函数,然后针对不同类型的对象定义不同的操作函数。
通过这种方式,我们可以在运行时根据对象的类型来调用相应的操作函数,实现多态效果。
除此之外,我们还可以使用函数表来实现多态。
函数表是一个包含函数指针的数组,每个函数指针指向一个操作函数。
我们可以将函数表作为对象的一个成员变量,在运行时根据对象类型选择相应的函数表,然后调用相应的操作函数。
总结来说,虽然C语言本身并不支持多态特性,但通过使用函数指针、结构体和函数表等技巧,我们仍然可以在C语言中实现多态效果。
这些方法需要我们在设计程序结构时仔细思考,合理利用指针和结构体的特性,才能实现灵活而高效的多态效果。
希望以上内容能够帮助您了解C语言中的多态实现方式。
c语言中多态的定义及实现方式

c语言中多态的定义及实现方式C语言是一种面向过程的编程语言,不支持面向对象编程的特性,如多态、继承和封装等。
但是,我们可以通过一些技巧来实现类似于面向对象编程中的多态性。
在本文中,我们将介绍C语言中多态的定义、实现方式以及举出一些例子。
1.多态的定义多态是面向对象编程中的一个重要概念。
它指的是不同对象对同一消息作出不同响应的能力。
在C语言中,我们可以通过函数指针、结构体和联合体等技术来实现多态性。
下面是多态的定义:多态是指在不同的对象上调用同一方法,而这些对象会根据所属类的不同产生不同的行为。
换句话说,多态是指一个接口,多种实现。
1.多态的实现方式在C语言中,我们可以通过以下方式来实现多态性:2.1 函数指针函数指针是指向函数的指针变量。
我们可以将不同的函数指针赋值给同一个函数指针变量,从而实现多态性。
例如:#include <stdio.h>void add(int a,int b){printf("%d + %d = %d\n", a, b, a + b);}void sub(int a,int b){printf("%d - %d = %d\n", a, b, a - b);}int main(){void(*p)(int,int);int a =10, b =5;p = add;p(a, b);p = sub;p(a, b);return0;}在上面的例子中,我们定义了两个函数add和sub,它们实现了两种不同的行为。
我们定义了一个函数指针p,它可以指向这两个函数。
在不同的情况下,我们将p 指向不同的函数,从而实现了多态性。
2.2 结构体结构体是一种自定义的数据类型,它可以包含多个不同类型的成员。
我们可以通过结构体来实现多态性。
例如:#include <stdio.h>typedef struct Animal{void(*speak)();} Animal;typedef struct Cat{Animal base;} Cat;typedef struct Dog{Animal base;} Dog;void cat_speak(){printf("Meow!\n");}void dog_speak(){printf("Woof!\n");}int main(){Cat cat;Dog dog;cat.base.speak = cat_speak;dog.base.speak = dog_speak;cat.base.speak();dog.base.speak();return0;}在上面的例子中,我们定义了一个Animal结构体和两个派生结构体Cat和Dog。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
c中实现多态的例子【篇一:c中实现多态的例子】下面是一个承上启下的例子。
一方面它是有关继承和运算符重载内容的综合应用的例子,通过这个例子可以进一步融会贯通前面所学的内容,另一方面又是作为讨论多态性的一个基础用例。
希望大家耐心、深入地阅读和消化这个程序,弄清其中的每一个细节。
[例12.1] 先建立一个point(点)类,包含数据成员x,y(坐标点)。
以它为基类,派生出一个circle(圆)类,增加数据成员r(半径),再以circle类为直接基类,派生出一个cylinder(圆柱体)类,再增加数据成员h(高)。
要求编写程序,重载运算符“ ”和“ ”,使之能用于输出以上类对象。
这个例题难度不大,但程序很长。
对于一个比较大的程序,应当分成若干步骤进行。
先声明基类,再声明派生类,逐级进行,分步调试。
1) 声明基类point类可写出声明基类point的部分如下:#include iostream //声明类pointclass pointpublic: point(float x=0,float y=0); //有默认参数的构造函数 void setpoint(float ,float); //设置坐标值 floatgetx( )const {return x;} //读x坐标 float gety( )const {return y;} //读y坐标 friend ostream operator (ostream ,const point //重载运算符“ ”protected: //受保护成员 float x, y;//下面定义point类的成员函数point::point(float a,float b) //point的构造函数{ //对x,y初始化 x=a; y=b;void point::setpoint(float a,float b) //设置x和y的坐标值{ //为x,y赋新值 x=a; y=b;//重载运算符“ ”,使之能输出点的坐标ostream operator (ostream output, const point p) output [ p.x , p.y ] endl; return output;}以上完成了基类point类的声明。
为了提高程序调试的效率,提倡对程序分步调试,不要将一个长的程序都写完以后才统一调试,那样在编译时可能会同时出现大量的编译错误,面对一个长的程序,程序人员往往难以迅速准确地找到出错位置。
要善于将一个大的程序分解为若干个文件,分别编译,或者分步调试,先通过最基本的部分,再逐步扩充。
现在要对上面写的基类声明进行调试,检查它是否有错,为此要写出main函数。
实际上它是一个测试程序。
int main( ) pointp(3.5,6.4); //建立point类对象p cout x= p.getx( ) ,y= p.gety( ) endl; //输出p的坐标值 p.setpoint(8.5,6.8); //重新设置p的坐标值cout p(new): p endl; //用重载运算符“ ”输出p点坐标 return0;}getx和gety函数声明为常成员函数,作用是只允许函数引用类中的数据,而不允许修改它们,以保证类中数据的安全。
数据成员x和y声明为protected,这样可以被派生类访问(如果声明为private,派生类是不能访问的)。
程序编译通过,运行结果为:x=3.5,y=6.4p(new):[8.5,6.8]测试程序检查了基类中各函数的功能,以及运算符重载的作用,证明程序是正确的。
2)声明派生类circle在上面的基础上,再写出声明派生类circle的部分:classcircle:public point //circle是point类的公用派生类public:circle(float x=0,float y=0,float r=0); //构造函数 voidsetradius(float ); //设置半径值 float getradius( )const; //读取半径值 float area ( )const; //计算圆面积 friend ostream operator (ostream ,const circle //重载运算符“ ”private: float radius;//定义构造函数,对圆心坐标和半径初始化circle::circle(float a,floatb,float r):point(a,b),radius(r){}//设置半径值voidcircle::setradius(float r){radius=r;}//读取半径值floatcircle::getradius( )const {return radius;}//计算圆面积float circle::area( )const return 3.14159*radius*radius;//重载运算符“ ”,使之按规定的形式输出圆的信息ostream operator (ostream output,const circle c) output center=[ c.x , c.y ],r=c.radius ,area= c.area( ) endl; return output;}为了测试以上circle 类的定义,可以写出下面的主函数:int main( ) circle c(3.5,6.4,5.2); //建立circle类对象c,并给定圆心坐标和半径 cout originalcircle:\nx= c.getx() , y= c.gety() , r= c.getradius( ) , area=c.area( ) endl; //输出圆心坐标、半径和面积 c.setradius(7.5); //设置半径值 c.setpoint(5,5); //设置圆心坐标值x,y cout new circle:\n c; //用重载运算符“ ”输出圆对象的信息 point pref=c; //pref是point 类的引用变量,被c初始化 cout pref: pref; //输出pref的信息return 0;}程序编译通过,运行结果为:original circle:(输出原来的圆的数据)x=3.5, y=6.4, r=5.2, area=84.9486new circle:(输出修改后的圆的数据)center=[5,5], r=7.5, area=176.714pref:[5,5] (输出圆的圆心“点”的数据)可以看到,在point类中声明了一次运算符“ ”重载函数,在circle 类中又声明了一次运算符“ ”,两次重载的运算符“ ”内容是不同的,在编译时编译系统会根据输出项的类型确定调用哪一个运算符重载函数。
main函数第7行用“cout ”输出c,调用的是在circle类中声明的运算符重载函数。
请注意main函数第8行:point pref = c;定义了 point类的引用变量pref,并用派生类circle对象c对其初始化。
前面我们已经讲过,派生类对象可以替代基类对象为基类对象的引用初始化或赋值(详情请查看:)。
现在 circle是point的公用派生类,因此,pref不能认为是c的别名,它得到了c的起始地址,它只是c中基类部分的别名,与c中基类部分共享同一段存储单元。
所以用“cout pref”输出时,调用的不是在circle中声明的运算符重载函数,而是在point中声明的运算符重载函数,输出的是“点”的信息,而不是“圆”的信息。
3) 声明circle的派生类cylinder前面已从基类point派生出circle类,现在再从circle派生出cylinder类。
class cylinder:public circle// cylinder是circle的公用派生类public: cylinder (float x=0,float y=0,float r=0,float h=0); //构造函数 void setheight(float ); //设置圆柱高 floatgetheight( )const; //读取圆柱高 loat area( )const; //计算圆表面积float volume( )const; //计算圆柱体积 friend ostream operator (ostream ,const cylinder //重载运算符 protected: float height;//圆柱高//定义构造函数cylinder::cylinder(float a,float b,floatr,float h):circle(a,b,r),height(h){}//设置圆柱高voidcylinder::setheight(float h){height=h;}//读取圆柱高float cylinder::getheight( )const {return height;}//计算圆表面积float cylinder::area( )const { return2*circle::area( )+2*3.14159*radius*height;}//计算圆柱体积float cylinder::volume()const {return circle::area()*height;}ostream operator (ostream output,const cylinder cy) outputcenter=[ cy.x , cy.y ],r= cy.radius ,h= cy.height \narea=cy.area( ) , volume= cy.volume( ) endl; return output;} //重载运算符“ ”可以写出下面的主函数:int main( ) cylindercy1(3.5,6.4,5.2,10);//定义cylinder类对象cy1 cout \noriginal cylinder:\nx= cy1.getx( ) , y= cy1.gety( ) , r= cy1.getradius( ) ,h= cy1.getheight( ) \narea= cy1.area() ,volume= cy1.volume() endl;//用系统定义的运算符“ ”输出cy1的数据 cy1.setheight(15);//设置圆柱高 cy1.setradius(7.5);//设置圆半径 cy1.setpoint(5,5);//设置圆心坐标值x,y cout \nnew cylinder:\n cy1;//用重载运算符“ ”输出cy1的数据 point pref=cy1;//pref是point类对象的引用变量cout \npref as a point: pref;//pref作为一个“点”输出 circlecref=cy1;//cref是circle类对象的引用变量 cout \ncref as a circle: cref;//cref作为一个“圆”输出 return 0;}运行结果如下:original cylinder:(输出cy1的初始值)x=3.5, y=6.4, r=5.2, h=10 (圆心坐标x,y。