C++中重载(overload)、覆盖(override)、隐藏
	原创 2009年11月15日 12:20:00
	重载(overload)
	我们在C中常常需要两个或多个函数完成相同的任务,但是参数的数据类型不同比如large(int i ,int  j),large(float i,float j)
	所以第一种解决方法就是large_int(),large_float(),但是这种方法比较笨拙,所以我们要找一种比较高明的方法,很幸运C++给我们提供了这样的一种机制—重载。
	我在同一个程序中可以使用同名的若干函数,但是当然要有限制啦,编译器可不比人聪明,我们要让编译器能唯一区分这些同名函数—函数签名。我们先看看如何区分两个同名函数:
	1.参数个数不同
	 2.参数个数相同,但是至少有一双对应的函数的类型不同。
	例如:void large(int x,int y) 和void large(int x,int y,int z)不同
	    :void large(int x,int y)和 void large(float x,float y)也不同
	我们再来看下一种情况:
	    :void large(int x,int y)和int large(int x,int y)
	好像这两个函数可以放到一个程序中,可是事实上当你把这两个函数放到一个程序的时候编译器就糊涂啦,调用一个large(3,4)我到底该不该返回值呢?其实学习过汇编语言的人都知道:我们在调用一个子程序的时候只需要存储的是需要的参数,返回地址。跟们与返回值没关系。所以返回值是完全不能区分两个函数名相同的函数滴。
	我们上边说的就是函数签名的内涵了,现在说说函数签名的概念吧,没办法,有许多人总喜欢问什么是***,总喜欢把一些很抽象的东西概念化,既然不能改变就随波逐流吧.
	函数签名:函数的名称及其参数类型组合在一起就定义了一个唯一的特性,称为函数签名。程序中的每一个函数都必须有一个唯一的函数签名。当编写包含函数调用的语句时,编译器就使用该调用创建一个函数签名,再把它和函数原型或者定义中可用的函数签名集相比较,如果找到了匹配的函数签名就建立索调用的函数,如果没有找到匹配的函数签名,就检查转换参数类型后是否有匹配的函数签名。
	有了这个基础,我们再看看下一种情况:
	    :void output(int a,int b=5)和void output(int a)这两个函数能在一个程序中吗?
	相信你一定知道了,答案是“不能”。因为这两个函数的签名相同了,当我调用output(5)时候编译器就糊涂了,两个都可以啊!
	总结一下:
	重载构成的条件:函数的参数类型,参数个数不同,才能构成函数的重载。但是返回类型不同不能构成函数重载,还要注意函数带有默认参数的情况。
覆盖(override)
	在说覆盖之前我们老规矩还要介绍介绍这个机制推出的背景:
	举个实际的例子,我们编写一个动物类:
	Dog ::wangwang!
	Cat::miaomiao!
	Cattle::MouMou!
	Animal speak
	Animal speak
	Animal speak
	解释一下这个简单的程序。我定义了一个动物类,狗,猫和牛都继承这个类,并重写了这个speak这个方法,当我dog.speak(),cat.speak(),cattle.speakl()都没有问题,但是这样太麻烦了,于是我想用一个统一的代码来表示这个说的动作,于是我定义了一个指针animal 分别指向dog,cat ,cattle 但是结果并没有我想象的那样,结果如下:
	这可不是我们所希望的。解释一下出现这个不希望结果的原因:
	#include
	using namespace std;
	class Animal
	{
	public:
	    void speak()
	    {
	        cout<<"Animal speak"< 
	    }
	};
	class Dog:public Animal
	{
	public:
	        void speak()
	    {
	        cout<<"Dog ::wangwang!"< 
	    }
	};
	class Cat:public Animal
	{
	public:
	        void speak()
	    {
	        cout<<"Cat::miaomiao!"< 
	    }
	};
	class Cattle:public Animal
	{
	public:
	     void speak()
	    {
	        cout<<"Cattle::MouMou!"< 
	    }
	};
	int main()
	{
	    Animal* animal;
	    Dog dog;
	    Cat cat;
	    Cattle cattle;
	    dog.speak();
	    cat.speak();
	    cattle.speak();
	    animal = &dog;
	    animal->speak();
	    animal = &cat;
	    animal->speak();
	    animal= &cattle;
	    animal->speak();
	    return 0;
	}
	当进行animal = &cat的时候,c++编译器进行了类型转换,认为animal中保存的对象就是Animal的对象,当然调用的是Ainmal的speak了。
	于是聪明的人们就想出了一个解决的办法:编译时不确定具体的调用函数,等到运行时,根据对象的类型来调用时哪一个函数。
	这个解决问题的能力就是c++提供的多态特性。
C++中多态特性是通过哪个关键字来定义的呢?相信你肯定知道答案:virtual.
	c++的编译器就是根据这个关键字来决定“是在编译时就确定调用哪个函数还是在执行时确定调用哪个函数呢”。
	编译时确定就称之为“早期绑定(early binding )"                 执行时确定称为"迟绑定(late binding).好啦,知道了这些知识我们动手改一改刚才的代码吧:
	其实就是在Animal类的speak方法上加一个关键字virtual. 结果绝对满足我们的预期:
	Dog ::wangwang!
	Cat::miaomiao!
	Cattle::MouMou!
	Dog ::wangwang!
	Cat::miaomiao!
	Cattle::MouMou!
	C++的多态性用一句话来概述:在基类的函数前加virtual关键字,在派生类重写该函数,运行时会根据对象的实际类型来调用相应的函数,如果对象是派生类就调用派生类的函数,如果是基类就调用基类的函数,与指针的类型无关,而是与指针实际指向的对象有关。
	再介绍一个很重要的概念:纯虚函数:纯虚函数是指明位不具体实现的虚成员函数,virtual void breathe() =0;这样就是一个纯虚函数。没有函数体,敖汉纯虚函数的类叫抽象类,如果想继承抽象类就必须要实现父类所有的纯虚函数,否则也要把这个函数在子类写成纯虚函数,这样派生类也成了抽象类,抽象类不能实例化对象。
	介绍了一大堆的知识相比你也烦了,但是当我介绍完前面的东西后“覆盖”也就呼之欲出了。
	覆盖:基类的虚函数A,有一个派生类重写了A,名称和参数列表与A完全相同,那么就成为函数的“覆盖”。
	看到了吧,所谓的覆盖就是我们前面介绍的speak().基类的speak是虚函数,那么派生类的speak无论是否有virtual关键字,都是虚函数,也就是说你的派生类的speak()前面可以加virtual也可以不加,效果都是一样的。
	总结一下覆盖的条件:
	                          1.基类函数必须是虚函数(使用virtual关键字声明)
	                          2.发生覆盖的函数要非别在派生类和基类中。
	                          3.函数名称和参数列表必须完全相同(不同的话就会引出下一个概念哟)
	                             那么这次跟返回值有没有关系呢,我实验了一下,当基类有一个虚函数virtual void A(),如果那么派生类中0可能存在int A(),因为编译会报错,但是如果是int A(int x)就可以啦,不过就不是覆盖了。所以再一次验证了根返回值没关系,不要考虑返回值。
	3.隐藏
	其实前边已经介绍了隐藏了,只不过没有明确说出来,所谓的隐藏就是指在派生类中具有与基类的同名函数(可不管参数列表是不是相同)从而在派生类中隐藏了基类的同名函数。也就是说在派生类调用的一定是派生类的函数,例如基类和派生类都有int A(),在派生类中一定是调用派生类的int A(),但是如果派生类中没有基类的int B()那么在派生类中调用B()时当然是调用基类的B()了,因为派生类中没有嘛,还有一种情况,就是如果基类与派生类的参数列表不同,那么无论基类的函数是不是虚函数基类的函数都会被隐藏。总结一下覆盖的发生条件:1.派生类的函数与基类的函数完全相同,但是基类函数不是虚函数,那么基类的函数将被隐藏。2.当派生类的函数与基类函数同名,但是具有不同的参数列表,那无论基类的函数是不是虚函数都会被隐藏。
	最后总结一下:发生在同一个类的同名函数,不同参数列表:重载
	                     发生在基类和派生类中的函数,基类函数是虚函数,派生类函数与基类函数名称与参数列表完全相同:覆盖
	                     派生类函数与基类函数 名称与参数列表 完全相同,但是基类函数不是虚函数:隐藏
	                     派生类函数与基类函数名称相同 但是参数列表不同,无论基类函数是不是virtual:隐藏。
	最后给出一个例子:
	 #include
	using namespace std;
	class Base
	{
	public:
	         virtual void  xfn(int i) 
	        {
	            cout<<"Base::xfn(int i)"< 
	        }
	        void yfn(float f)
	        {
	            cout<<"Base::yfn(float f)"< 
	        }
	        void zfn()
	        {
	            cout<<"Base::zfn()"< 
	        }
	};
	class Derived :public Base
	{
	public:
	         void   xfn(int i)//覆盖了xfn的函数
	        {
	             cout<<"Derived::xfn(int i)"< 
	        }
	        void yfn(int c) //隐藏了基类yfn的函数
	        {
	             cout<<"Derived::yfn(int i)"< 
	        }
	        void zfn() //隐藏了基类zfn的函数
	        {
	               cout<<"Drived::zfn()"< 
	        }
	};
	int main()
	{
	    Derived d;
	    Base* pB = &d;
	    Derived* pD = &d;
	    pB->xfn(5);
	    pD->xfn(5);
	     pB->yfn(3.14f);
	    pD->yfn(3.14f);
	    pB->zfn();
	    pD->zfn();
	    return 0;
	}
	结果如下:
	 Derived::xfn(int i)
	Derived::xfn(int i)
	Base::yfn(float f)
	Derived::yfn(int i)
	Base::zfn()
	Drived::zfn()

京公网安备 11010502036488号