地球人都知道面向对象的三个主要特征是封装,继承,多态.前面两个概念顾名思义,比较容易理解,封装就是所有的东东都给封装到一个个的class中,并通过public,private等访问修饰符提供一些权限控制.继承嘛就是子类能拥有父类的非私有的成员变量和函数,就像你能继承你老子的财产一样啊.多态光看名字可不好理解,可没变态那么通俗易懂啊.其实前面说的三个特性还有另外的版本,比如:数据抽象,继承,动态绑定.说法不一样但表示的意思完全一样.
多态可以简单的理解为有同一种东西有多种形态,比如雨,雪,冰都是水的不同形态.在代码里体现就是调用相同的函数但函数实际上执行的是不同的操作.而实现这种功能的最主要手段是虚函数,当然不一定非得是它,用接口也一样.只不过大多数时候还是用虚函数实现多态多一点.
C#虚函数和多态
虚函数就是在普通的函数前面加个关键字virtual就行了.如果虚函数没用来实现多态的话完全可以当作一般的函数用,没啥区别
下面举个例子,有父亲Father,有子类Son,Daughter
class Father
{
public virtual void ShowMsg() //这就是虚函数
{
Console.WriteLine("This is father.");
}
}
class Son :Father
{
/*此处加个override到后面就能实现多态,多态就是virtual和override的搭配使用*/
publicoverride void ShowMsg()
{
Console.WriteLine("This is Son.");
}
}
classDaughter :Father
{
/*此处没用override而是用new,其实也可以去掉new,因为默认就是当作添加个new在那,当然其实还可以用virtual,这样如果谁继承了此类还可以通过配合override来实现多态*/
publicnew void ShowMsg()
{
Console.WriteLine("This is Daughter.");
}
}
测试函数
Public void ShowYourName(Father fa)
{
fa.ShowMsg();
}
static void Main(string[] args)
{
Fahter father = new Father();
Father son = new Son();
Father daughter = new Daughter();
ShowYourName(father ); //结果this is father
ShowYourName(son); //结果this is son
ShowYourName(daughter ); /*结果this is father.上面的两个函数是体现了多态,但Daughter类中没用override去重写函数,所以不能实现多态.*/
}
由上面可知当调用相同的函数ShowYourName()时执行了不同的操作.这就是多态.其实觉得还是用动态绑定这术语更容易理解点,就是函数的参数类型fa只有在运行时才真正确定它的类型.因为子类is a 特殊的父类,所以可以先用父类来代替子类的类型.不过C#里面没指针的概念,不容易解释清楚,等会举C++的例子时用指针解释更容易明白.
如果要不用虚函数而是用接口或抽象来也一样可以实现多态.
比如public abstract class Father
{
public abstract void ShowMsg();
}
public interface Father
{
void ShowMsg();
}
只不过如果是继承抽象函数必须用override来重写,不能用new.继承接口的话直接像普通函数一样重新定义下就行,只不过注意修饰符必须是public.
另外这两者自然不能直接Father fa = new Father()这样实例化.其他操作就都一样了.也一样实现多态了.
C++虚函数与多态
假如有父类Father,有子类Son,Duaghter.
class Father
{
public :
int age;
virtual void ShowMsg(){cout<<"This is father.";} //virtual必须有
};
class Son :public Father
{
public :
virtual void ShowMsg(){cout<<"This is Son.";} //virtual关键字可有可无
};
class Daughter :public Father
{
public :
void ShowMsg(){cout<<"This is Daughter."<<endl;}
};
void ShowYourName(Father* fa)
{
fa->ShowMsg();
}
int _tmain(int argc, _TCHAR* argv[])
{
Father fa;
Son son;
Daughter da;
Father* pFa;
pFa = &fa;
ShowYourName(pFa); //结果this is father
pFa = &son;
ShowYourName(pFa); //结果this is son
pFa = &da;
ShowYourName(pFa); //结果this is daughter
return 0;
}
貌似C++里面没有像C#一样可以用new关键字来隐藏子类的函数.不过有个操作可以实现类似的功能.
假如这样Father *pFa; Daughter da;
pFa = &((Father)da);
ShowYourName(pFa); //这样结果就会为this is father,和上面的示例不一样,上面示例是this is daughter.
C++虚函数实现多态的原理
看了示例后还有点云里雾里的.虚函数怎么就莫名其妙的实现了多态呢?
这就要说到虚拟函数表(vtable)
我们想下当创建Father,Son,Daughter这三个类时,在内存中是怎么表示的呢.
类Father,在内存中这样表示的呢.你用sizeof(fa)查看下发现结果为8,其中4个字节是用来装int型数据,还有4个字节是装着一个指针vptr,如果64位上指针应该是8个字节了啊.
vptr指向一个虚拟函数表,此表中又装着另外一个指针(*ShowMsg)(),它指向Father::ShowMsg().(这就是真正保存函数的地方,(如果不是虚函数而是一般的函数也保存在这个区域,只不过不是通过指针来找).
类Son继承了Father,所以会把Father内存中的东东拷贝过来,所以sizeof一下也是8.其中4字节保存int类型.4字节保存指针vptr,同样指向虚拟函数表,但这里会做这样一个操作.如果发现Son中有重写了虚拟函数,就会更新此表,否则不更新.由于Son重写了虚拟函数,所以表中的指针(*ShowMsg)()指向的内容变了,指向了Son::ShowMsg()
而类Daughter跟Son的操作完全一样.
这样当pFa = &da;表示指针指向类Daughter的内存,由于虚函数列表中对应的函数是Daughter::ShowMsg().所以就明白了为啥pFa->ShowMsg();调用的是类Daughter中的这函数了.但假如Daughter没有重写虚函数,整个类Daughter就为空,只是继承Father,这样的话它的虚函数表中所指的函数就是Father::ShowMsg()了
现在虚函数是怎么实现多态的是弄明白了,那刚才上面说的那个与C#中有new关键字隐藏子类函数的操作是咋回事呢?
Father *pFa; Daughter da;
pFa = &((Father)da); //注意这个地方(Father)da 进行类型转换时实际上是完全拷贝了类Father内存中的内容.所以虚函数列表指向的函数就是Father::ShowMsg()了
ShowYourName(pFa);
分享到:
相关推荐
我们判断一个类是否支持多态,只需要看它有没有虚函数便可以了。 当编译一个C++程序时,计算机的内存被分成了4个区域,一个包括程序的代码,一个包括所有的全局变量,一个是堆栈,还有一个是堆(heap),我们称堆...
我们判断一个类是否支持多态,只需要看它有没有虚函数便可以了。 当编译一个C++程序时,计算机的内存被分成了4个区域,一个包括程序的代码,一个包括所有的全局变量,一个是堆栈,还有一个是堆(heap),我们称堆...
某公司雇员(Employee)...SalesManager类,pay()函数则是经理的固定奖金额的一半,加上部门总销售额与提成比例之积,这是业绩工资。 编程实现工资管理。特别注意pay()的定义和调用方法:先用同名覆盖,再用运行时多态。
本文特别适合有 C++ 基础却没有太多精力学习 C# 的读者。 关于作者 Aisha Ikram 我现在在英国一家软件公司任技术带头人。我是计算机科学的硕士。我主要使用 .NET 1.1/2.0, C#, VB.NET, ASP.NET, VC++ 6, MFC, ...
·洞察 C++和其他语言(例如Java、C#、C)之间的不同。此举有助于那些来自其他语言阵营的开发人员消化吸收 C++ 式的各种解法。 目录: 译序 中英简繁术语对照 目录 序言 致谢 导读 1.让自己习惯C++ ...
这个库的目的是要在java中提供一个类似parsec, spirit的库,这种组合子库并非c++的专利,java/c#也可以做到。这个库还将在java5.0上被改写,类型安全上它将也不再逊色于c++。 那么,为什么叫“函数式”呢?java是...
Notes:数据private类型,大部分方法public类型;...C#和其他面向对象一样,重载,多态,虚函数,抽象类这些特性都有; 重载和C++中的重载毫无区别,只有一个问题是在重载大小比较运算符(<, >=, >, <=
,不懂就百度,另外别拿C#和java的对比c++的重写 1.重写必须继承,重载不用。2.重写的方法名,参数数目相同,参数类型兼容,重载的方法名相同,参数列表不同。3.重写的方法修饰符大于等于父类的方法,重载和修饰符...
易语言是c++写的,所以要在在这之前必须要弄懂c++中什么是重写,重载,隐藏,3个不同的概念,不懂就百度,另外别拿C#和java的对比c++的重写 1.重写必须继承,重载不用。 2.重写的方法名,参数数目相同,参数类型兼容...
答:折构函数式销毁一个类的函数,虚函数是为了C++的动态绑定而设计的。 描述你的编程风格? 答:类名首字母大写,常量一般全部大写,给自己的代码加注释。 控制流程? 答:控制流程一般使用if判断条件。有第二分支...
本文特别适合有 C++基础却没有太多精力学习 C#的读者。 目录 野比经验交流系列(C#) 前言 .................................................................................4 说明 ......................