乐之者体感开发

好知者不如乐之者,体感开发请点击:http://www.enjoythis.net/

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

几天前看了多态,感觉不怎么好!因为就是虚函数的重写

但是,看看大牛们的文章,才感觉到压力!多态果然不一样,今天把他们的日志转载过来共那些没有入门,(多态方面)的同学学习!

1. 函数多态

     也就是我们常说的函数重载(function overloading)。基于不同的参数列表,同一个函数名字可以指向不同的函数定义: 

// overload_poly.cpp

#include <iostream>
#include <string>

// 定义两个重载函数

 1 int my_add(int a, int b)
2 {
3 return a + b;
4 }
5 int my_add(int a, std::string b)
6 {
7 return a + atoi(b.c_str());
8 }
9 int main()
10 {
11 int i = my_add(1, 2); // 两个整数相加
12 int s = my_add(1, "2"); // 一个整数和一个字符串相加
13 std::cout << "i = " << i << "\n";
14 std::cout << "s = " << s << "\n";
15 }



     根据参数列表的不同(类型、个数或兼而有之),my_add(1, 2)和my_add(1, "2")被分别编译为对my_add(int, int)和my_add(int, std::string)的调用。实现原理在于编译器根据不同的参数列表对同名函数进行名字重整,而后这些同名函数就变成了彼此不同的函数。比方说,也许某个编译器会将my_add()函数名字分别重整为my_add_int_int()和my_add_int_str()。

2. 宏多态

     带变量的宏可以实现一种初级形式的静态多态:  
// macro_poly.cpp

#include <iostream>
#include <string>

// 定义泛化记号:宏ADD
#define ADD(A, B) (A) + (B);

1 int main()
2 {
3 int i1(1), i2(2);
4 std::string s1("Hello, "), s2("world!");
5 int i = ADD(i1, i2); // 两个整数相加
6 std::string s = ADD(s1, s2); // 两个字符串“相加”
7 std::cout << "i = " << i << "\n";
8 std::cout << "s = " << s << "\n";
9 }



     当程序被编译时,表达式ADD(i1, i2)和ADD(s1, s2)分别被替换为两个整数相加和两个字符串相加的具体表达式。整数相加体现为求和,而字符串相加则体现为连接。程序的输出结果符合直觉:  
     1 + 2 = 3
     Hello, + world! = Hello, world!

3. 动态多态

     这就是众所周知的的多态。现代面向对象语言对这个概念的定义是一致的。其技术基础在于继承机制和虚函数。例如,我们可以定义一个抽象基类Vehicle和两个派生于Vehicle的具体类Car和Airplane:

 1 // dynamic_poly.h
2 #include <iostream>
3 // 公共抽象基类Vehicle
4 class Vehicle
5 {
6 public:
7 virtual void run() const = 0;
8 };
9 // 派生于Vehicle的具体类Car
10 class Car: public Vehicle
11 {
12 public:
13 virtual void run() const
14 {
15 std::cout << "run a car\n";
16 }
17 };
18 // 派生于Vehicle的具体类Airplane
19 class Airplane: public Vehicle
20 {
21 public:
22 virtual void run() const
23 {
24 std::cout << "run a airplane\n";
25 }
26 };
27 客户程序可以通过指向基类Vehicle的指针(或引用)来操纵具体对象。通过指向基类对象的指针(或引用)来调用一个虚函数,会导致对被指向的具体对象之相应成员的调用:
28 // dynamic_poly_1.cpp
29 #include <iostream>
30 #include <vector>
31 #include "dynamic_poly.h"
32 // 通过指针run任何vehicle
33 void run_vehicle(const Vehicle* vehicle)
34 {
35 vehicle->run(); // 根据vehicle的具体类型调用对应的run()
36 }
37 int main()
38 {
39 Car car;
40 Airplane airplane;
41 run_vehicle(&car); // 调用Car::run()
42 run_vehicle(&airplane); // 调用Airplane::run()
43 }
44 此例中,要害的多态接口元素为虚函数run()。由于run_vehicle()的参数为指向基类Vehicle的指针,因而无法在编译期决定使用哪一个版本的run()。在运行期,为了分派函数调用,虚函数被调用的那个对象的完整动态类型将被访问。这样一来,对一个Car对象调用run_vehicle(),实际上将调用Car::run(),而对于Airplane对象而言将调用Airplane::run()。
45 或许动态多态最吸引人之处在于处理异质对象集合的能力:
46 // dynamic_poly_2.cpp
47 #include <iostream>
48 #include <vector>
49 #include "dynamic_poly.h"
50 // run异质vehicles集合
51 void run_vehicles(const std::vector<Vehicle*>& vehicles)
52 {
53 for (unsigned int i = 0; i < vehicles.size(); ++i)
54 {
55 vehicles[i]->run(); // 根据具体vehicle的类型调用对应的run()
56 }
57 }
58 int main()
59 {
60 Car car;
61 Airplane airplane;
62 std::vector<Vehicle*> v; // 异质vehicles集合
63 v.push_back(&car);
64 v.push_back(&airplane);
65 run_vehicles(v); // run不同类型的vehicles
66 }



     在run_vehicles()中,vehicles[i]->run()依据正被迭代的元素的类型而调用不同的成员函数。这从一个侧面体现了面向对象编程风格的优雅。

4. 静态多态

     假如说动态多态是通过虚函数来表达共同接口的话,那么静态多态则是通过“彼此单独定义但支持共同操作的具体类”来表达共同性,换句话说,必须存在必需的同名成员函数。  
     我们可以采用静态多态机制重写上一节的例子。这一次,我们不再定义vehicles类层次结构,相反,我们编写彼此无关的具体类Car和Airplane(它们都有一个run()成员函数): 

View Code
 1 // static_poly.h
2 #include <iostream>
3 //具体类Car
4 class Car
5 {
6 public:
7 void run() const
8 {
9 std::cout << "run a car\n";
10 }
11 };
12 //具体类Airplane
13 class Airplane
14 {
15 public:
16 void run() const
17 {
18 std::cout << "run a airplane\n";
19 }
20 };
21 run_vehicle()应用程序被改写如下:
22
23 // static_poly_1.cpp
24 #include <iostream>
25 #include <vector>
26 #include "static_poly.h"
27 // 通过引用而run任何vehicle
28 template <typename Vehicle>
29 void run_vehicle(const Vehicle& vehicle)
30 {
31 vehicle.run(); // 根据vehicle的具体类型调用对应的run()
32 }
33
34 int main()
35 {
36 Car car;
37 Airplane airplane;
38 run_vehicle(car); // 调用Car::run()
39 run_vehicle(airplane); // 调用Airplane::run()
40 }
41 现在Vehicle用作模板参数而非公共基类对象(事实上,这里的Vehicle只是一个符合直觉的记号而已,此外别无它意)。经过编译器处理后,我们最终会得到run_vehicle<Car>()和 run_vehicle<Airplane>()两个不同的函数。这和动态多态不同,动态多态凭借虚函数分派机制在运行期只有一个run_vehicle()函数。
42 我们无法再透明地处理异质对象集合了,因为所有类型都必须在编译期予以决定。不过,为不同的vehicles引入不同的集合只是举手之劳。由于无需再将集合元素局限于指针或引用,我们现在可以从执行性能和类型安全两方面获得好处:
43 // static_poly_2.cpp
44 #include <iostream>
45 #include <vector>
46 #include "static_poly.h"
47 // run同质vehicles集合
48 template <typename Vehicle>
49 void run_vehicles(const std::vector<Vehicle>& vehicles)
50 {
51 for (unsigned int i = 0; i < vehicles.size(); ++i)
52 {
53 vehicles[i].run(); // 根据vehicle的具体类型调用相应的run()
54 }
55 }
56 int main()
57 {
58 Car car1, car2;
59 Airplane airplane1, airplane2;
60 std::vector<Car> vc; // 同质cars集合
61 vc.push_back(car1);
62 vc.push_back(car2);
63 //vc.push_back(airplane1); // 错误:类型不匹配
64 run_vehicles(vc); // run cars
65 std::vector<Airplane> vs; // 同质airplanes集合
66 vs.push_back(airplane1);
67 vs.push_back(airplane2);
68 //vs.push_back(car1); // 错误:类型不匹配
69 run_vehicles(vs); // run airplanes
70 }



5. 两种多态机制的结合使用 

     在一些高级C++应用中,我们可能需要结合使用动态多态和静态多态两种机制,以期达到对象操作的优雅、安全和高效。例如,我们既希望一致而优雅地处理vehicles的run问题,又希望“安全而高效”地完成给飞行器(飞机、飞艇等)进行“空中加油”这样的高难度动作。为此,我们首先将上面的vehicles类层次结构改写如下: 

 1 // dscombine_poly.h
2 #include <iostream>
3 #include <vector>
4 // 公共抽象基类Vehicle
5 class Vehicle
6 {
7 public:
8 virtual void run() const = 0;
9 };
10 // 派生于Vehicle的具体类Car
11 class Car: public Vehicle
12 {
13 public:
14 virtual void run() const
15 {
16 std::cout << "run a car\n";
17 }
18 };
19 // 派生于Vehicle的具体类Airplane
20 class Airplane: public Vehicle
21 {
22 public:
23 virtual void run() const
24 {
25 std::cout << "run a airplane\n";
26 }
27
28 void add_oil() const
29 {
30 std::cout << "add oil to airplane\n";
31 }
32 };
33 // 派生于Vehicle的具体类Airship
34 class Airship: public Vehicle
35 {
36 public:
37 virtual void run() const
38 {
39 std::cout << "run a airship\n";
40 }
41
42 void add_oil() const
43 {
44 std::cout << "add oil to airship\n";
45 }
46 };



我们理想中的应用程序可以编写如下:
  

 1 // dscombine_poly.cpp
2 #include <iostream>
3 #include <vector>
4 #include "dscombine_poly.h"
5 // run异质vehicles集合
6 void run_vehicles(const std::vector<Vehicle*>& vehicles)
7 {
8 for (unsigned int i = 0; i < vehicles.size(); ++i)
9 {
10 vehicles[i]->run(); // 根据具体的vehicle类型调用对应的run()
11 }
12 }
13 // 为某种特定的aircrafts同质对象集合进行“空中加油”
14 template <typename Aircraft>
15 void add_oil_to_aircrafts_in_the_sky(const std::vector<Aircraft>& aircrafts)
16 {
17 for (unsigned int i = 0; i < aircrafts.size(); ++i)
18 {
19 aircrafts[i].add_oil();
20 }
21 }
22 int main()
23 {
24 Car car1, car2;
25 Airplane airplane1, airplane2;
26 Airship airship1, airship2;
27 std::vector<Vehicle*> v; // 异质vehicles集合
28 v.push_back(&car1);
29 v.push_back(&airplane1);
30 v.push_back(&airship1);
31 run_vehicles(v); // run不同种类的vehicles
32 std::vector<Airplane> vp; // 同质airplanes集合
33 vp.push_back(airplane1);
34 vp.push_back(airplane2);
35 add_oil_to_aircrafts_in_the_sky(vp); // 为airplanes进行“空中加油”
36 std::vector<Airship> vs; // 同质airships集合
37 vs.push_back(airship1);
38 vs.push_back(airship2);
39 add_oil_to_aircrafts_in_the_sky(vs); // 为airships进行“空中加油”
40 }



     我们保留了类层次结构,目的是为了能够利用run_vehicles()一致而优雅地处理异质对象集合vehicles的run问题。同时,利用函数模板add_oil_to_aircrafts_in_the_sky<Aircraft>(),我们仍然可以处理特定种类的vehicles — aircrafts(包括airplanes和airships)的“空中加油”问题。其中,我们避开使用指针,从而在执行性能和类型安全两方面达到了预期目标。

posted on 2011-10-15 14:44  伟征  阅读(337)  评论(0编辑  收藏  举报