首页 > 代码库 > 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++ 多态的实现