C++中的静态多态和动态多态(一)

2014-11-24 11:54:00 · 作者: · 浏览: 3
今天的C++已经是个多重泛型编程语言(multiparadigm programming lauguage),一个同时支持过程形式(procedural)、面向对象形式(object-oriented)、函数形式(functional)、泛型形式(generic)、元编程形式(metaprogramming)的语言。 这些能力和弹性使C++成为一个无可匹敌的工具,但也可能引发使用者的某些迷惑,比如多态。在这几种编程泛型中,面向对象编程、泛型编程以及很新的元编程形式都支持多态的概念,但又有所不同。 C++支持多种形式的多态,从表现的形式来看,有虚函数、模板、重载等,从绑定时间来看,可以分成静态多态和动态多态,也称为编译期多态和运行期多态。
本文即讲述这其中的异同。注意泛型编程和元编程通常都是以模板形式实现的,因此在本文中主要介绍基于面向对象的动态多态和基于模板编程的静态多态两种形式。另外其实宏也可以认为是实现静态多态的一种方式,实现原理就是全文替换,但C++语言本身就不喜欢宏,这里也忽略了“宏多态”。
什么是动态多态?
动态多态的设计思想:对于相关的对象类型,确定它们之间的一个共同功能集,然后在基类中,把这些共同的功能声明为多个公共的虚函数接口。各个子类重写这些虚函数,以完成具体的功能。客户端的代码(操作函数)通过指向基类的引用或指针来操作这些对象,对虚函数的调用会自动绑定到实际提供的子类对象上去。
从上面的定义也可以看出,由于有了虚函数,因此动态多态是在运行时完成的,也可以叫做运行期多态,这造就了动态多态机制在处理异质对象集合时的强大威力(当然,也有了一点点性能损失)。
看代码:
复制代码
1 namespace DynamicPoly
2 {
3 class Geometry
4 {
5 public:
6 virtual void Draw()const = 0;
7 };
8
9 class Line : public Geometry
10 {
11 public:
12 virtual void Draw()const{ std::cout << "Line Draw()\n"; }
13 };
14
15 class Circle : public Geometry
16 {
17 public:
18 virtual void Draw()const{ std::cout << "Circle Draw()\n"; }
19 };
20
21 class Rectangle : public Geometry
22 {
23 public:
24 virtual void Draw()const{ std::cout << "Rectangle Draw()\n"; }
25 };
26
27 void DrawGeometry(const Geometry *geo)
28 {
29 geo->Draw();
30 }
31
32 //动态多态最吸引人之处在于处理异质对象集合的能力
33 void DrawGeometry(std::vector vecGeo)
34 {
35 const size_t size = vecGeo.size();
36 for(size_t i = 0; i < size; ++i)
37 vecGeo[i]->Draw();
38 }
39 }
40
41 void test_dynamic_polymorphism()
42 {
43 DynamicPoly::Line line;
44 DynamicPoly::Circle circle;
45 DynamicPoly::Rectangle rect;
46 DynamicPoly::DrawGeometry(&circle);
47
48 std::vector vec;
49 vec.push_back(&line);
50 vec.push_back(&circle);
51 vec.push_back(&rect);
52 DynamicPoly::DrawGeometry(vec);
53 }
复制代码
动态多态本质上就是面向对象设计中的继承、多态的概念。动态多态中的接口是显式接口(虚函数),比如,
复制代码
1 void DoSomething(Widget& w)
2 {
3 if( w.size() > 0 && w != someNastyWidget)
4 {
5 Widget temp(w);
6 temp.normalize();
7 temp.swap(w);
8 }
9 }
复制代码
对于上面的代码,这要求:
由于w的类型被声明为Widget,所以w必须支持Widget接口,且通常可以在 源码中找出这些接口(比如Widget.h),因此这些接口也就是显示接口;
Widget可能只是一个基类,他有子类,也就是说Widget的接口有可能是虚函数(比如上面的normalize),此时对接口的调用就表现出了运行时多态;
什么是静态多态?
静态多态的设计思想:对于相关的对象类型,直接实现它们各自的定义,不需要共有基类,甚至可以没有任何关系。只需要各个具体类的实现中要求相同的接口声明,这里的接口称之为隐式接口。客户端把操作这些对象的函数定义为模板,当需要操作什么类型的对象时,直接对模板指定该类型实参即可(或通过实参演绎获得)。
相对于面向对象 编程中,以显式接口和运行期多态(虚函数)实现动态多态,在模板编程及泛型编程中,是以隐式接口和编译器多态来实现静态多态。
看代码:
复制代码
1 namespace StaticPoly
2 {
3 class Line
4 {
5 public:
6 void Draw()const{ std::cout << "Line Draw()\n"; }
7 };
8
9 class Circle
10 {
11 public:
12 void Draw(const char* name=NULL)const{ std::cout << "Circle Draw()\n"; }
13 };
14
15 class Rectangle
16 {
17 public:
18 void Draw(int i = 0)const{ std::cout << "Rectangle Draw()\n"; }
19 };
20
21 template
22 void DrawGeometry(const Geometry& geo)
23 {
24 geo.Draw();
25 }
26
27 template
28 void DrawGeometry(std::vector vecGeo)
29 {
30 const size_t size = vecGeo.