多态polymorphism是指具有多种形态的情况,它能根据单一的标记关联不同的行为。多态是面向对象程序设计的基础。在面向对象程序设计中的多态是一种运行时的多态。C++中有两种多态,称为动多态(运行时多态)和静多态(编译时多态),而静多态主要通过模板来实现,宏也是实现静多态的一种途径。其实在做软件设计时静多态的威力也是非常强大的,只不过我们经常对它疏忽了而已。
    动多态的设计思想:对于相关的对象类型,确定它们之间的一个共同功能集,然后在基类中,把这些共同的功能声明为多个公共的虚函数接口。各个子类重写这些虚函数,以完成具体的功能。客户端的代码(操作函数)通过指向基类的引用或指针来操作这些对象,对虚函数的调用会自动绑定到你实际提供的子类对象上去。
    下面以几何对象的设计为例。对各种几何对象如圆、矩形、直线等,都有一些共同的操作,比如画出几何对象,有重心等,我们把这些接口抽象成虚函数放在所谓的抽象基类GeoObj中,具体的几何对象类则继承这个抽象基类。如下:

//dynahier.hpp:几何类的定义
#ifndef __GEOOBJ_H__
#define __GEOOBJ_H__
#include "coord.hpp"
class GeoObj{ //几何对象的公共抽象基类
public:
virtual void draw() const=; //画出几何对象
virtual Coord center_of_gravity() const=; //返回几何对象的重心
//...
};
class Circle : public GeoObj{ //具体的几何对象类:圆
public:
virtual void draw() const;
virtual Coord center_of_gravity() const;
//...
};
class Line : public GeoObj{ //直线类
public:
virtual void draw() const;
virtual Coord center_of_gravity() const;
//...
};
//...
#endif

客户端的使用如下:

//dynapoly.cpp:客户端代码
#include "dynahier.hpp"
#include <vector>
void myDraw(GeoObj const& obj){ //画任意一个GeoObj对象
obj.draw(); //根据对象类型来调用对应的draw()
}
Coord distance(GeoObj const& x1,GeoObj const& x2){ //计算两个GeoObj对象的重心之间的距离
Coord c=x1.center_of_gravity()-x2.center_of_gravity();
return c.abs(); //返回坐标的绝对值
}
void drawElems(std::vector<GeoObj*> const& elems){ //画出属于异类集合的GeoObj对象
for(std::size_t i=;i<elems.size();++i)
elems[i]->draw(); //根据元素类型来调用相应的draw()
}
int main(){
Line l;
Circle c,c1,c2;
myDraw(l); //myDraw(GeoObj&) => Line::draw()
myDraw(c); //myDraw(GeoObj&) => Circle::draw()
distance(c1,c2); //distance(GeoObj&,GeoObj&)
distance(l,c); //distance(GeoObj&,GeoObj&)
std::vector<GeoObj*> coll; //元素类型互异的集合
coll.push_back(&l); //插入一条直线
coll.push_back(&c); //插入一个圆
drawElems(coll); //画不同种类的GeoObj对象
return ;
}

静多态的设计思想: 对于相关的对象类型,直接实现它们各自的定义,不需要基类。只是隐式地规定各个具体类的实现中相同功能的接口名要相同。客户端把操作这些对象的函数定义为模板,你需要操作什么类型的对象,直接对模板指定该类型实参即可(或通过实参演绎获得)。
    改写上面的设计,如下:

//statichier.hpp:几何类的定义
#ifndef __GEOOBJ_H__
#define __GEOOBJ_H__
#include "coord.hpp"
class Circle{ //具体的几何对象类Circle,并没有派生自其他类
public:
void draw() const; //非虚函数
Coord center_of_gravity() const;
//...
};
class Line{ //直线类Line
public:
void draw() const;
Coord center_of_gravity() const;
//...
};
//...
#endif
//staticpoly.cpp:客户端代码
#include "statichier.hpp"
#include <vector>
template<typename GeoObj>
void myDraw(GeoObj const& obj){ //画任意一个GeoObj对象
obj.draw(); //根据对象类型来调用对应的draw()
}
template<typename GeoObj1,typename GeoObj2>
Coord distance(GeoObj1 const& x1,GeoObj2 const& x2){ //计算两个GeoObj对象的重心之间的距离
Coord c=x1.center_of_gravity()-x2.center_of_gravity();
return c.abs(); //返回坐标的绝对值
}
template<typename GeoObj>
void drawElems(std::vector<GeoObj> const& elems){ //画出属于异类集合的GeoObj对象
for(std::size_t i=;i<elems.size();++i)
elems[i].draw(); //根据元素类型来调用相应的draw()
}
int main(){
Line l;
Circle c,c1,c2;
myDraw(l); //myDraw<Line>(Line&) => Line::draw()
myDraw(c); //myDraw<Circle>(Circle&) => Circle::draw()
distance(c1,c2); //distance<Circle,Circle>(Circle&,Circle&)
distance(l,c); //distance<Line,Circle>(Line&,Circle&)
//std::vector<GeoObj*> coll; //错误:异类集合在这里不允许
std::vector<Line> coll; //正确:同类集合在这里是允许的
coll.push_back(l); //插入一条直线
drawElems(coll); //画出所有直线
return ;
}

两种多态设计范式的比较:
    (1)动多态的特点:通过继承实现、接口预先在基类中确定、是动态的(运行期绑定接口)。
    优点:能处理异类集合(容器中存储基类指针即可)、可执行代码比较小(只需一个多态函数)、可以完全编译而不需要发布源码。
    缺点:不能提前检查类型的安全性(如向容器中插入错误类型的对象)、性能低(有层层继承)、耦合性高(继承的耦合性高于组合)。
    (2)静多态的特点:通过模板实现(宏也是实现静多态的一种途径)、接口没有预先确定而只是隐式地规定、是静态的(编译期绑定接口)。
    优点:具体类可以只实现需要的接口、生成代码性能高(无需通过指针的间接调用,非虚函数具有更多的内联机会)、有更好的类型安全性(类型在编译期就进行检查)、耦合性低(各个类相互独立)、集合的元素类型不再局限于指针。
    缺点:不能处理异类集合、可执行代码比较大(代码膨胀)、模板库源码需要发布、对模板实参类型有约束(比如需要该类型实现了operator<)。
    现实中我们可以组合两种多态来做设计:从公共基类派生不同的子类,从而能够处理属于异类集合的不同对象。而需要操作具体某种类型的对象时,使用模板来实现。
    实际上,泛型程序设计依赖的就是静多态。C++中的一个泛型程序设计杰作就是STL。STL库中的设计思想是把对象的集合抽象为容器,把对对象的操作抽象为算法。算法和容器都是模板(即静多态方案),这样算法可以独立出来,而不是容器的成员函数。一个算法可以被多种容器使用。为了让容器能够使用算法,从容器中抽象出迭代器的概念,通过把容器的迭代器传给算法,就可以在容器上执行算法的行为。
    迭代器是泛型程序设计的粘合剂,它由容器提供并能被算法所使用。迭代器之所以能作粘合剂,是由于容器为迭代器提供了一些特定的接口,而算法所使用的正是这些接口。一般把这样的接口称为concept(即约束)。
    从原则上讲,我们也可以用动多态来实现STL,然而这肯定会带来很多的麻烦。与迭代器的概念相比,动多态的虚函数调用机制将会是一种重量级的实现机制,比如增加一层基于虚函数的接口层,通常这会对性能和效率产生很大的影响,有可能是几个数量级。可见,静多态也是一种威力非常强大的工具,只要应用得好,通过C++的模板,我们可以设计出抽象程度高、性能很好的软件组件。

转自:http://blog.csdn.net/zhoudaxia/article/details/4476056

c++ 静态多态与动态多态的更多相关文章

  1. C++中的静态多态和动态多态

    C++中的静态多态和动态多态 今天的C++已经是个多重泛型编程语言(multiparadigm programming lauguage),一个同时支持过程形式(procedural).面向对象形式( ...

  2. c++ 宏多态 动态多态和静态多态(转载)

    转载出处:通道 多态(polymorphism)一词最初来源于希腊语polumorphos,含义是具有多种形式或形态的情形.在程序设计领域,一个广泛认可的定义是“一种将不同的特殊行为和单个泛化记号相关 ...

  3. C++ //多态 //静态多态:函数重载 和 运算符重载 属于静态多态 ,复用函数名 //动态多态:派生类和虚函数实现运行时多态

    1 //多态 2 //静态多态:函数重载 和 运算符重载 属于静态多态 ,复用函数名 3 //动态多态:派生类和虚函数实现运行时多态 4 5 //静态多态和动态多态的区别 6 //静态多态的函数地址早 ...

  4. OC基础6:多态、动态类型和动态绑定

    "OC基础"这个分类的文章是我在自学Stephen G.Kochan的<Objective-C程序设计第6版>过程中的笔记. 1.关于SEL类型的数据: (1).SEL ...

  5. C++ 动态多态

    背景 以前的学习,只是简单地知道:面向对象的三大特性(封装.继承.多态) ,在项目开发中,用到了多态而自己却不知道. 多态(Polymorphism)按字面的意思就是"多种状态". ...

  6. java静态代理与动态代理简单分析

    原创作品,可以转载,但是请标注出处地址http://www.cnblogs.com/V1haoge/p/5860749.html 1.动态代理(Dynamic Proxy) 代理分为静态代理和动态代理 ...

  7. c++ :OOP之静态类型与动态类型

    所谓静态类型即类型指针或引用的字面类型:而动态类型即类型指针或引用的实际类型. 这一对概念一般发生在基类和派生类之间. 如: class Base { ..... } class Derived : ...

  8. Swift 静态派发和动态派发

    前言 方法是 Swift 中的一个重要概念,方法允许你把需要复用的代码封装进方法中,这样当你调用方法时,实际上你的想法是执行方法中的那些代码,方法的出现极大的提高了方法的复用性. Swift 工程的环 ...

  9. java中代理,静态代理,动态代理以及spring aop代理方式,实现原理统一汇总

    若代理类在程序运行前就已经存在,那么这种代理方式被成为 静态代理 ,这种情况下的代理类通常都是我们在Java代码中定义的. 通常情况下, 静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类. ...

随机推荐

  1. 场景切换特效Transition——Cocos2d-x学习历程(十二)

    Transition 场景切换 在游戏中通常会用到一些场景的切换,比如从加载界面切换到欢迎界面.游戏中的所有场景存放在一个栈中,有且只有一个场景可以处于激活状态.直接replaceScene(即不适用 ...

  2. Qt 圆角矩形+鼠标左键拖动窗口

    #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> namespace Ui { class MainWind ...

  3. Using Apache with SSL1(转载)

    SSL/TLS/WTLS原理 作者:yawl < yawl@nsfocus.com >主页:http://www.nsfocus.com日期:2001-02-19 一 前言 首先要澄清一下 ...

  4. JavaWeb核心编程之(三.5)HTTP请求和接受表单数据

    HTTP简介WEB浏览器与WEB拂去其之间的一问一答的交互过程, 必须遵循一定的规则,这个规则就是HTTP协议HTTP是hypertext transfer protocol(超文本传输协议)的简写, ...

  5. mysql--自动增长

    create table teacher( t_id int primary key auto_increment, #auto_increment 自动增长 需要整型,还需要索引 t_name va ...

  6. CentOS(minimal)+Nginx+php+mysql实现宿主访问虚拟机

    /* 1.先解决上网 1-1.参照联网状态文件 # cd /etc/sysconfig/network-scripts # vi ifcfg-eth0 1-2.编辑联网状态文件 详见图1-2 1-3. ...

  7. python初学笔记(二)

    注释: 任何时候,我们都可以给程序加上注释.注释是用来说明代码的,给自己或别人看,而程序运行的时候,Python解释器会直接忽略掉注释,所以,有没有注释不影响程序的执行结果,但是影响到别人能不能看懂你 ...

  8. 转 ——eclipse下进行Python开发 环境配置

    python for eclipse插件安装1.下载python for eclipsepython for eclipse下载地址,如:org.python.pydev.feature-1.6.3. ...

  9. SQL Server Service Borker 1

    1.消息类型定义: 消息类型,是信息交换的模板.create message type message_type_name validattion = well_formed_xml; 2.约定定义: ...

  10. Linux bug 14258279: scheduling clock overflows in 208 days

    早上同事反映数据库不能用.无法正常登录主机.多次尝试后终于登上主机,检查系统日志发现下述错误: BUG: soft lockup - CPU#5 stuck for 17163091988s! 貌似是 ...