新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论高级C/C++编程、代码重构(Refactoring)、极限编程(XP)、泛型编程等话题
    [返回] 中文XML论坛 - 专业的XML技术讨论区计算机技术与应用『 C/C++编程思想 』 → C++多态技术[转帖] 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 3083 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: C++多态技术[转帖] 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     firstway 帅哥哟,离线,有人找我吗?
      
      
      威望:5
      等级:大三暑假(2个月背完了红宝书)(版主)
      文章:92
      积分:947
      门派:Lilybbs.net
      注册:2005/10/31

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给firstway发送一个短消息 把firstway加入好友 查看firstway的个人资料 搜索firstway在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看firstway的博客楼主
    发贴心情 C++多态技术[转帖]

    C++多态技术

    摘要

    本文介绍了C++中的各种多态性,重点阐述了面向对象的动态多态和基于模板的静
    态多态,并初探两种技术的结合使用。

    关键词

    函数多态 宏多态 动态多态 静态多态

    导言

    多态(polymorphism)一词最初来源于希腊语polumorphos,含义是具有多种形式
    或形态的情形。在程序设计领域,一个广泛认可的定义是“一种将不同的特殊行为
    和单个泛化记号相关联的能力”。和纯粹的面向对象程序设计语言不同,C++中的
    多态有着更广泛的含义。除了常见的通过类继承和虚函数机制生效于运行期的动态
    多态(dynamic polymorphism)外,模板也允许将不同的特殊行为和单个泛化记号
    相关联,由于这种关联处理于编译期而非运行期,因此被称为静态多态(static
    polymorphism)。

    事实上,带变量的宏和函数重载机制也允许将不同的特殊行为和单个泛化记号相关
    联。然而,习惯上我们并不将它们展现出来的行为称为多态(或静态多态)。今天,
    当我们谈及多态时,如果没有明确所指,默认就是动态多态,而静态多态则是指基
    于模板的多态。不过,在这篇以C++各种多态技术为主题的文章中,我们首先还是
    回顾一下C++社群争论已久的另一种“多态”:函数多态(function polymorphis
    m),以及更不常提的宏多态(macro polymorphism)。

    函数多态

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

    // overload_poly.cpp

    #include <iostream>
    #include <string>

    // 定义两个重载函数

    int my_add(int a, int b)
    {
        return a + b;

    }

    int my_add(int a, std::string b)
    {
        return a + atoi(b.c_str());

    }

    int main()
    {
        int i = my_add(1, 2);                // 两个整数相加
        int s = my_add(1, "2");              // 一个整数和一个字符串相加
        std::cout << "i = " << i << "\n";
        std::cout << "s = " << s << "\n";

    }

    根据参数列表的不同(类型、个数或兼而有之),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()。

    宏多态

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

    // macro_poly.cpp

    #include <iostream>
    #include <string>

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

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

    }

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

    1 + 2 = 3
    Hello, + world! = Hello, world!

    动态多态

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

    // dynamic_poly.h

    #include <iostream>

    // 公共抽象基类Vehicle
    class Vehicle
    {
    public:
        virtual void run() const = 0;

    };

    // 派生于Vehicle的具体类Car
    class Car: public Vehicle
    {
    public:
        virtual void run() const
        {
            std::cout << "run a car\n";
        }

    };

    // 派生于Vehicle的具体类Airplane
    class Airplane: public Vehicle
    {
    public:
        virtual void run() const
        {
            std::cout << "run a airplane\n";
        }

    };

    客户程序可以通过指向基类Vehicle的指针(或引用)来操纵具体对象。通过指向
    基类对象的指针(或引用)来调用一个虚函数,会导致对被指向的具体对象之相应
    成员的调用:

    // dynamic_poly_1.cpp

    #include <iostream>
    #include <vector>
    #include "dynamic_poly.h"

    // 通过指针run任何vehicle
    void run_vehicle(const Vehicle* vehicle)
    {
        vehicle->run();            // 根据vehicle的具体类型调用对应的run()

    }

    int main()
    {
        Car car;
        Airplane airplane;
        run_vehicle(&car);         // 调用Car::run()
        run_vehicle(&airplane);    // 调用Airplane::run()

    }

    此例中,关键的多态接口元素为虚函数run()。由于run_vehicle()的参数为指向基
    类Vehicle的指针,因而无法在编译期决定使用哪一个版本的run()。在运行期,为
    了分派函数调用,虚函数被调用的那个对象的完整动态类型将被访问。这样一来,
    对一个Car对象调用run_vehicle(),实际上将调用Car::run(),而对于Airplane对
    象而言将调用Airplane::run()。

    或许动态多态最吸引人之处在于处理异质对象集合的能力:  

    // dynamic_poly_2.cpp

    #include <iostream>
    #include <vector>
    #include "dynamic_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()
        }

    }

    int main()
    {
        Car car;
        Airplane airplane;
        std::vector<Vehicle*> v;    // 异质vehicles集合
        v.push_back(&car);
        v.push_back(&airplane);
        run_vehicles(v);            // run不同类型的vehicles

    }

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

    静态多态

    如果说动态多态是通过虚函数来表达共同接口的话,那么静态多态则是通过“彼此
    单独定义但支持共同操作的具体类”来表达共同性,换句话说,必须存在必需的同
    名成员函数。

    我们可以采用静态多态机制重写上一节的例子。这一次,我们不再定义vehicles类
    层次结构,相反,我们编写彼此无关的具体类Car和Airplane(它们都有一个run()
    成员函数):

    // static_poly.h

    #include <iostream>

    //具体类Car
    class Car
    {
    public:
        void run() const
        {
            std::cout << "run a car\n";
        }

    };

    //具体类Airplane
    class Airplane
    {
    public:
        void run() const
        {
            std::cout << "run a airplane\n";
        }

    };

    run_vehicle()应用程序被改写如下:  

    // static_poly_1.cpp

    #include <iostream>
    #include <vector>
    #include "static_poly.h"

    // 通过引用而run任何vehicle
    template <typename Vehicle>
    void run_vehicle(const Vehicle& vehicle)
    {
        vehicle.run();            // 根据vehicle的具体类型调用对应的run()

    }

    int main()
    {
        Car car;
        Airplane airplane;
        run_vehicle(car);         // 调用Car::run()
        run_vehicle(airplane);    // 调用Airplane::run()

    }

    现在Vehicle用作模板参数而非公共基类对象(事实上,这里的Vehicle只是一个符
    合直觉的记号而已,此外别无它意)。经过编译器处理后,我们最终会得到
    run_vehicle<Car>()和 run_vehicle<Airplane>()两个不同的函数。这和动态多态
    不同,动态多态凭借虚函数分派机制在运行期只有一个run_vehicle()函数。

    我们无法再透明地处理异质对象集合了,因为所有类型都必须在编译期予以决定。
    不过,为不同的vehicles引入不同的集合只是举手之劳。由于无需再将集合元素局
    限于指针或引用,我们现在可以从执行性能和类型安全两方面获得好处:

    // static_poly_2.cpp

    #include <iostream>
    #include <vector>
    #include "static_poly.h"

    // run同质vehicles集合
    template <typename Vehicle>
    void run_vehicles(const std::vector<Vehicle>& vehicles)
    {
        for (unsigned int i = 0; i < vehicles.size(); ++i)
        {
            vehicles[i].run();            // 根据vehicle的具体类型调用相应的run()
        }

    }

    int main()
    {
        Car car1, car2;
        Airplane airplane1, airplane2;

        std::vector<Car> vc;              // 同质cars集合
        vc.push_back(car1);
        vc.push_back(car2);
        //vc.push_back(airplane1);        // 错误:类型不匹配
        run_vehicles(vc);                 // run cars

        std::vector<Airplane> vs;         // 同质airplanes集合
        vs.push_back(airplane1);
        vs.push_back(airplane2);
        //vs.push_back(car1);             // 错误:类型不匹配
        run_vehicles(vs);                 // run airplanes

    }

    两种多态机制的结合使用  

    在一些高级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 car\n";
        }

    };

    // 派生于Vehicle的具体类Airplane
    class Airplane: public Vehicle
    {
    public:
        virtual void run() const
        {
            std::cout << "run a airplane\n";
        }
     

        void add_oil() const
        {
            std::cout << "add oil to airplane\n";
        }

    };

    // 派生于Vehicle的具体类Airship
    class Airship: public Vehicle
    {
    public:
        virtual void run() const
        {
            std::cout << "run a airship\n";
        }

        void add_oil() const
        {
            std::cout << "add oil to airship\n";
        }

    };

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

    // 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这样的网
    络论坛上,此类话题争论至今仍随处可见。曾经有人将动态多态称为inclusion
    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”。文后的“参考文献”单元给出了这个网页链接。

    可能你是第一次看到宏多态这个术语。不必讶异,也许我就是造出这个术语的“第
    一人”。显然,带变量的宏(或类似于函数的宏或伪函数宏)的替换机制除了免除
    小型函数的调用开销之外,也表现出了类似的多态性。在我们上面的例子中,字符
    串相加所表现出来的符合直觉的连接操作,事实上是由底部运算符重载机制支持的。
    值得指出的是,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程序库已经走在了我
    们的前面。

    参考文献

    1. David Vandevoorde, Nicolai M. Josuttis, C++ Templates: The Complete
       Guide, Addison Wesley, 2002.

    2. Chris Neumann, CS193d (Summer 2003) C++ and Object-Oriented
       Programming, http://www.stanford.edu/class/cs193d/, 2003.


       收藏   分享  
    顶(0)
      




    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2006/4/27 16:51:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 C/C++编程思想 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/5/10 9:59:45

    本主题贴数1,分页: [1]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    78.125ms