C++基础之C++多态技术(1)(2)
两种多态机制的结合使用
在一些高级C++应用中,我们可能需要结合使用动态多态和静态多态两种机制,以期达到对象操作的优雅、安全和高效。例如,我们既希望一致而优雅地处理vehicles的run问题,又希望“安全而高效”地完成给飞行器(飞机、飞艇等)进行“空中加油”这样的高难度动作。为此,我们首先将上面的vehicles类层次结构改写如下:
- // dscombine_poly.h
- #include <iostream>
- #include <vector>
- // 公共抽象基类Vehicle
- class Vehicle
- {
- public:
- virtual void run() const = 0;
- };
- // 派生于Vehicle的具体类Car
- class Car: public Vehicle
- {
- public:
- virtual void run() const
- {
- std::cout << "run a carn";
- }
- };
- // 派生于Vehicle的具体类Airplane
- class Airplane: public Vehicle
- {
- public:
- virtual void run() const
- {
- std::cout << "run a airplanen";
- }
- void add_oil() const
- {
- std::cout << "add oil to airplanen";
- }
- };
- // 派生于Vehicle的具体类Airship
- class Airship: public Vehicle
- {
- public:
- virtual void run() const
- {
- std::cout << "run a airshipn";
- }
- void add_oil() const
- {
- std::cout << "add oil to airshipn";
- }
- };
我们理想中的应用程序可以编写如下:
- // dscombine_poly.cpp
- #include <iostream>
- #include <vector>
- #include "dscombine_poly.h"
- // run异质vehicles集合
- void run_vehicles(const std::vector<Vehicle*>& vehicles)
- {
- for (unsigned int i = 0; i < vehicles.size(); ++i)
- {
- vehicles[i]->run(); // 根据具体的vehicle类型调用对应的run()
- }
- }
- // 为某种特定的aircrafts同质对象集合进行“空中加油”
- template <typename Aircraft>
- void add_oil_to_aircrafts_in_the_sky(const std::vector<Aircraft>& aircrafts)
- {
- for (unsigned int i = 0; i < aircrafts.size(); ++i)
- {
- aircrafts[i].add_oil();
- }
- }
- int main()
- {
- Car car1, car2;
- Airplane airplane1, airplane2;
- Airship airship1, airship2;
- std::vector<Vehicle*> v; // 异质vehicles集合
- v.push_back(&car1);
- v.push_back(&airplane1);
- v.push_back(&airship1);
- run_vehicles(v); // run不同种类的vehicles
- std::vector<Airplane> vp; // 同质airplanes集合
- vp.push_back(airplane1);
- vp.push_back(airplane2);
- add_oil_to_aircrafts_in_the_sky(vp); // 为airplanes进行“空中加油”
- std::vector<Airship> vs; // 同质airships集合
- vs.push_back(airship1);
- vs.push_back(airship2);
- add_oil_to_aircrafts_in_the_sky(vs); // 为airships进行“空中加油”
- }
我们保留了类层次结构,目的是为了能够利用run_vehicles()一致而优雅地处理异质对象集合vehicles的run问题。同时,利用函数模板add_oil_to_aircrafts_in_the_sky<Aircraft>(),我们仍然可以处理特定种类的vehicles — aircrafts(包括airplanes和airships)的“空中加油”问题。其中,我们避开使用指针,从而在执行性能和类型安全两方面达到了预期目标。
结语
长期以来,C++社群对于多态的内涵和外延一直争论不休。在comp.object这样的网络论坛上,此类话题争论至今仍随处可见。曾经有人将动态多态(dynamic polymorphism)称为inclusion polymorphism,而将静态多态(static polymorphism)称为parametric polymorphism或parameterized polymorphism。
我注意到2003年斯坦福大学公开的一份C++ and Object-Oriented Programming教案中明确提到了函数多态概念:Function overloading is also referred to as function polymorphism as it involves one function having many forms。文后的“参考文献”单元给出了这个网页链接。
可能你是第一次看到宏多态(macro polymorphism)这个术语。不必讶异 — 也许我就是造出这个术语的“第一人”。显然,带变量的宏(或类似于函数的宏或伪函数宏)的替换机制除了免除小型函数的调用开销之外,也表现出了类似的多态性。在我们上面的例子中,字符串相加所表现出来的符合直觉的连接操作,事实上是由底部运算符重载机制(operator overloading)支持的。值得指出的是,C++社群中有人将运算符重载所表现出来的多态称为ad hoc polymorphism。
David Vandevoorde和Nicolai M. Josuttis在他们的著作C++ Templates: The Complete Guide一书中系统地阐述了静态多态和动态多态技术。因为认为“和其他语言机制关系不大”,这本书没有提及“宏多态”(以及“函数多态”)。(需要说明的是,笔者本人是这本书的繁体中文版译者之一,本文正是基于这本书的第14章The Polymorphic Power of Templates编写而成)
动态多态只需要一个多态函数,生成的可执行代码尺寸较小,静态多态必须针对不同的类型产生不同的模板实体,尺寸会大一些,但生成的代码会更快,因为无需通过指针进行间接操作。静态多态比动态多态更加类型安全,因为全部绑定都被检查于编译期。正如前面例子所示,你不可将一个错误的类型的对象插入到从一个模板实例化而来的容器之中。此外,正如你已经看到的那样,动态多态可以优雅地处理异质对象集合,而静态多态可以用来实现安全、高效的同质对象集合操作。
静态多态为C++带来了泛型编程(generic programming)的概念。泛型编程可以认为是“组件功能基于框架整体而设计”的模板编程。STL就是泛型编程的一个典范。STL是一个框架,它提供了大量的算法、容器和迭代器,全部以模板技术实现。从理论上讲,STL的功能当然可以使用动态多态来实现,不过这样一来其性能必将大打折扣。
静态多态还为C++社群带来了泛型模式(generic patterns)的概念。理论上,每一个需要通过虚函数和类继承而支持的设计模式都可以利用基于模板的静态多态技术(甚至可以结合使用动态多态和静态多态两种技术)而实现。正如你看到的那样,Andrei Alexandrescu的天才作品Modern C++ Design: Generic Programming and Design Patterns Applied(Addison-Wesley)和Loki程序库已经走在了我们的前面。
- 上一篇:C++编程对缓冲区的理解
- 下一篇:C语言基础之文件操作基本常识