重写(override)(发生在子类与父类中):
虚函数允许子类重新定义成员函数,而子类重新定义父类的做法称为覆盖(override),或者称为重写。
重写的话分两种:直接重写成员函数和重写虚函数,只有重写了虚函数的才能算作是体现了C++多态性
重载(overload)(发生在同一个类中):
允许有多个同名的函数。而这些函数的参数列表不同,
允许参数个数不同,参数类型不同,或者两者都不同。不关返回值的事,仅仅是返回值不同编译不通过。
编译器会根据这些函数的不同列表,将同名的函数的名称做修饰,从而生成一些不同名称的预处理函数,来实现同名函数调用时的重载问题。
多态:
多态与非多态的实质区别就是函数地址是早绑定还是晚绑定。
如果函数的调用,在编译器编译期间就可以确定函数的调用地址,并生产代码,是静态的,简称为编译时多态。
而如果函数调用的地址不能在编译器期间确定,需要在运行时才确定,这就属于晚绑定,简称为运行时多态。
最常见的用法就是声明基类的指针,利用该指针指向任意一个子类对象,调用相应的虚函数,可以根据指向的子类的不同而实现不同的方法。
如果没有使用虚函数的话,即没有利用C++多态性,则利用基类指针调用相应的函数的时候,将总被限制在基类函数本身,而无法调用到子类中被重写过的函数。因为没有多态性,函数调用的地址将是一定的,而固定的地址将始终调用到同一个函数,这就无法实现一个接口,多种方法的目的了。
例子如下:
#include<iostream>
using namespace std;
class A
{
public:
void foo()
{
printf("1\n");
}
virtual void fun()
{
printf("2\n");
}
};
class B : public A
{
public:
void foo()
{
printf("3\n");
}
void fun()
{
printf("4\n");
}
};
int main(void)
{
A a;
B b;
A *p = &a;
p->foo();
p->fun();
p = &b;
p->foo();
p->fun();
return 0;
}
输出结果为:1、2、1、4;
分析:A* p = &a导致前2个输出为1、2这不难理解,因为p本身就是指向基类A的指针。
而A* p = &b ,
由于p是基类指针(A*)指向子类对象b;故当调用p->foo()时,p->foo()由于指针是个基类指针,指向是一个固定偏移量的函数,因此此时指向的就只能是基类的foo()函数的代码了,因此输出的结果还是1。
当调用p->fun()时,这个就得关系到虚函数表的知识了。(虚函数表将在下一篇博客解说)由于p所指向的是一个虚函数,并不直接调用A类中的fun().而是寻找虚函数表,找到对应的虚函数,没错,就是B::fun();故输出结果为:4;
再通过下面这个源码来理解一下重写虚函数与成员函数的区别
//小结:1、有virtual才可能发生多态现象
// 2、不发生多态(无virtual)调用就按原类型调用
#include<iostream>
using namespace std;
class Base
{
public:
virtual void f(float x)
{
cout<<"Base::f(float)"<< x <<endl;
}
void g(float x)
{
cout<<"Base::g(float)"<< x <<endl;
}
void h(float x)
{
cout<<"Base::h(float)"<< x <<endl;
}
};
class Derived : public Base
{
public:
virtual void f(float x)
{
cout<<"Derived::f(float)"<< x <<endl; //重写虚函数
}
void g(int x)
{
cout<<"Derived::g(int)"<< x <<endl; //重写成员函数
}
void h(float x)
{
cout<<"Derived::h(float)"<< x <<endl; //重写成员函数
}
};
int main(void)
{
Derived d;
Base *pb = &d;
Derived *pd = &d;
// Good : behavior depends solely on type of the object
pb->f(3.14f); // Derived::f(float) 3.14
pd->f(3.14f); // Derived::f(float) 3.14
// Bad : behavior depends on type of the pointer
pb->g(3.14f); // Base::g(float) 3.14
pd->g(3.14f); // Derived::g(int) 3
// Bad : behavior depends on type of the pointer
pb->h(3.14f); // Base::h(float) 3.14
pd->h(3.14f); // Derived::h(float) 3.14
return 0;
}
上面的程序中:
(1)函数Derived::f(float)覆盖了Base::f(float)。
(2)函数Derived::g(int)隐藏了Base::g(float),。
(3)函数Derived::h(float)隐藏了Base::h(float),而不是覆盖。
总而言之.判断一个子类是否实现了多态,只需要一句话:
如果子类中有一个成员函数,
1、名字与基类一模一样,
2、参数个数,参数类型均相同、
3、基类函数是虚函数(其实子类若是成功重写了虚函数其本身也是虚函数)
则必定是实现了多态。
反之。仅仅是名字相同而不满足第2.3中的其中一点,则是重写成员函数。基类的成员函数将被隐藏
本文由Cout_Sev 搜集整理
转载注明出处
http://blog.youkuaiyun.com/cout_sev
谢谢!