首页 > 代码库 > C++ 多态的实现
C++ 多态的实现
重写(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中的其中一点,则是重写成员函数。基类的成员函数将被隐藏
C++ 多态的实现