详解C++ 多态的两种形式(静态、动态)
1.多态的概念与分类
多态(Polymorphisn)是面向对象程序设计(OOP)的一个重要特征。多态字面意思为多种状态。在面向对象语言中,一个接口,多种实现即为多态。C++中的多态性具体体现在编译和运行两个阶段。编译时多态是静态多态,在编译时就可以确定使用的接口。运行时多态是动态多态,具体引用的接口在运行时才能确定。
静态多态和动态多态的区别其实只是在什么时候将函数实现和函数调用关联起来,是在编译时期还是运行时期,即函数地址是早绑定还是晚绑定的。静态多态是指在编译期间就可以确定函数的调用地址,并生产代码,这就是静态的,也就是说地址是早绑定。静态多态往往也被叫做静态联编。 动态多态则是指函数调用的地址不能在编译器期间确定,需要在运行时确定,属于晚绑定,动态多态往往也被叫做动态联编。
2.多态的作用
为何要使用多态呢?封装可以使得代码模块化,继承可以扩展已存在的代码,他们的目的都是为了代码重用。而多态的目的则是为了接口重用。静态多态,将同一个接口进行不同的实现,根据传入不同的参数(个数或类型不同)调用不同的实现。动态多态,则不论传递过来的哪个类的对象,函数都能够通过同一个接口调用到各自对象实现的方法。
3.静态多态
静态多态往往通过函数重载和模版(泛型编程)来实现,具体可见下面代码:
#include <iostream> using namespace std; //两个函数构成重载 int add(int a, int b) { cout<<"in add_int_int()"<<endl; return a + b; } double add(double a, double b) { cout<<"in add_double_doube()"<<endl; return a + b; } //函数模板(泛型编程) template <typename T> T add(T a, T b) { cout<<"in func tempalte"<<endl; return a + b; } int main() { cout<<add(1,1)<<endl; //调用int add(int a, int b) cout<<add(1.1,1.1)<<endl; //调用double add(double a, double b) cout<<add<char>('A',' ')<<endl; //调用模板函数,输出小写字母a }
程序输出结果:
in add_int_int()
2
in add_double_doube()
2.2
in func tempalte
a
4.动态多态
动态多态最常见的用法就是声明基类的指针,利用该指针指向任意一个子类对象,调用相应的虚函数,可以根据指向的子类的不同而调用不同的方法。如果没有使用虚函数,即没有利用C++多态性,则利用基类指针调用相应函数的时候,将总被限制在基类函数本身,而无法调用到子类中被重写过的函数。因为没有多态性,函数调用的地址将是一定的,而固定的地址将始终调用同一个函数,这就无法“实现一个接口,多种实现”的目的了。
#include <iostream> using namespace std; class Base { public: virtual void func() { cout << "Base::fun()" << endl; } }; class Derived : public Base { public: virtual void func() { cout << "Derived::fun()" << endl; } }; int main() { Base* b=new Derived; //使用基类指针指向派生类对象 b->func(); //动态绑定派生类成员函数func Base& rb=*(new Derived); //也可以使用引用指向派生类对象 rb.func(); }
程序输出结果:
Derived::fun()
Derived::fun()
通过上面的例子可以看出,在使用基类指针或引用指向子类对象时,调用的函数是子类中重写的函数,这样就实现了运行时函数地址的动态绑定,即动态联编。动态多态是通过“继承+虚函数”来实现的,只有在程序运行期间(非编译期)才能判断所引用对象的实际类型,根据其实际类型调用相应的方法。具体格式就是使用virtual关键字修饰类的成员函数时,指明该函数为虚函数,并且派生类需要重新实现该成员函数,编译器将实现动态绑定。
以上就是详解C++ 多态的两种形式(静态、动态)的详细内容,更多关于C++ 静态多态和动态多态的资料请关注我们其它相关文章!