说到面向对象特性之一“多态”,以我的水平已经说不出太多新意了。相信很多程序员代码K多了,做梦都在“多态中”运行着。常规的多态是C++语义内置支持的一种特性,通过虚函数可以实现这个特性,为了后面以示区别,我们姑且把这种多态称为“动态多态”或”运行期多态“,而本文总主要想讨论下“静态多态”,也可以叫“编译期多态”,同时一起来看下,静态多态会给我们带来哪些惊喜之处,拭目以待吧。

首先看个正常通过虚函数实现多态的常规例子,如下所示,很简单明了无需多言。
[cpp] view plaincopy
  1. #include <iostream>
  2. #include <string>
  3. class BasicClassic
  4. {
  5. public:
  6. virtual void Print() = 0;
  7. };
  8. class DerivedClassic1 : public BasicClassic
  9. {
  10. public:
  11. DerivedClassic1() {}
  12. virtual void Print() {
  13. std::cout << "DerivedClassic1 Print" << std::endl;
  14. }
  15. };
  16. class DerivedClassic2 : public BasicClassic
  17. {
  18. public:
  19. DerivedClassic2() {}
  20. virtual void Print() {
  21. std::cout << "DerivedClassic2 Print" << std::endl;
  22. }
  23. };
通过虚函数,在运行时通过虚函数表指针,通过索引找到对应函数,然后进行调用,所以前面我们称之为动态多态或运行时多态。那静态多态又是怎么回事,如何实现呢?答案是模版。如果熟悉COM的同学应该见过框架中很多通过模版来实现多态的实现,看下如下的实现:
[cpp] view plaincopy
  1. template<typename Derived>
  2. class Basic
  3. {
  4. public:
  5. inline void Print() {
  6. SelfCast()->Print();
  7. }
  8. protected:
  9. inline Derived* SelfCast() {
  10. return static_cast <Derived*>(this);
  11. }
  12. };
  13. class Derived1 : public Basic<Derived1>
  14. {
  15. public:
  16. Derived1() {}
  17. inline void Print() {
  18. std::cout << "Derived1 Print" << std::endl;
  19. }
  20. };
  21. class Derived2 : public Basic<Derived2>
  22. {
  23. public :
  24. Derived2() {}
  25. inline void Print() {
  26. std::cout << "Derived2 Print" << std::endl;
  27. }
  28. static std::string Name() {
  29. return "Derived2 Class" ;
  30. }
  31. };
具体使用的代码:
[cpp] view plaincopy
  1. Basic<Derived1>* der1 = new Derived1();
  2. der1->Print();
  3. Basic<Derived2>* der2 = new Derived2();
  4. der2->Print();
输出结果:
Derived1 Print
Derived2 Print
这里实现的关键是SelfCast函数,通过static_cast把当前对象强制转换为具体指定的子类对象,这里是Derived1。其实实现的原理很简单,不难理解,我们需要重点讨论的是,这么样实现的多态跟常规虚函数的做法到底有什么不同,有什么优势?
大家应该都知道,虚函数的使用会带来额外的开销,具有虚函数的class类型都需要一张虚函数表,而每多一个虚函数,对应类型的对象的大小就会增加4bytes(32位机器下),夸张的试想一下如果有10个父类,每个父类都有100个虚函数的情况下,每个对象会增加多少?
4x10x100=4000bytes!
除了空间上的开销,每个虚函数的调用在时间上都会比普通函数多一次整形加法和一次指针间接引用,也就是时间上的开销
这种开销虽然在绝大多数的应用中都是可以忽略不计的,但是总会存在一些对性能与开销无比在意的关键代码。根据28法则,应用中80%的时间都是在运行其中20%的代码,那么有时候对这20%代码的优化也许会带来显著的改善。
回到正题,我们把传统的实现方式称为动态多态,而模板方式的实现则是静态多态,归纳下他们的区别:
  1. 动态多态的多态性是在运行期决定的,而静态多态是在编译期就决定的
  2. 动态多态的实现需要更多空间上的开销,每个对象会因为一个虚函数而增加4bytes,静态多态则没有这个问题
  3. 动态多态的实现需要更多的时间开销,虚函数的调用在时间上都会比普通函数多一次整形加法和一次指针间接引用,静态多态中的调用则跟普通函数的调用开销相同
  4. 动态多态(虚函数)是C++编译器内置支持的一种实现方式,而静态多态则会额外带来一些使用的复杂性
  5. 动态多态中虚函数不能通过内联来优化执行效率,而静态多态中则可以通过内联来进一步优化函数执行效率
综上所述,在实际使用中,到底选择哪种实现方式,要因需而异,如果没有特别的性能需求时,完全没有必要为了写的更酷而去使用模版的方式来实现,反而得不偿失,但如果针对特别需求或关键性能的代码,则可以考虑这种优化。
另外再看一种使用方式,模版还可以实现static函数的类似多态特性,如下所示:
[cpp] view plaincopy
  1. template <typename Derived>
  2. class Basic
  3. {
  4. public :
  5. Basic() {  }
  6. inline void Print() {
  7. std::cout << Basic<Derived>::Name() << std::endl;
  8. SelfCast()->Print();
  9. }
  10. static std::string Name() {
  11. return Derived::Name();
  12. }
  13. protected :
  14. inline Derived* SelfCast() {
  15. return static_cast <Derived*>( this);
  16. }
  17. };
  18. class Derived1 : public Basic<Derived1>
  19. {
  20. public :
  21. Derived1() {}
  22. inline void Print() {
  23. std::cout << "Derived1 Print" << std::endl;
  24. }
  25. static std::string Name() {
  26. return "Derived1 Class" ;
  27. }
  28. };
也就是说针对某些希望定义为static的函数,当你希望能在基类抽象方法中根据当前对象具体类型来使用子类相应static函数时,如上方法可以达成你的目的,这未尝不是一种不错的实现方式。
ok,静态多态就说到这,延伸还有哪些特定的应用,欢迎大家一起讨论。
=========================================================================

c++的模板是个很强很暴力的东西,可以轻松地模拟多态。下面举个例子:
====================================================================
#include <iostream>
#include <string>

using namespace std;

class StarDecorator//注意:与多态不同,不需要有类阶层(简单说不需要继承啥接口)
{
public:
   void printHead()
   {
       cout << "**********" << endl;
   }
   void printTail()
   {
       cout << "**********" << endl;
   }
};

class AddDecorator
{
public:
   void printHead()
   {
       cout << "++++++++++" << endl;
   }
   void printTail()
   {
       cout << "++++++++++" << endl;
   }
};

template<class Decorator>
class Printer
{
public:
   void print(const string& str)
   {
       decorator.printHead();
       cout << str << endl;
       decorator.printTail();
   }
private:
   Decorator decorator;
};

int main(int argc, char* argv[])
{
   Printer<StarDecorator> p1;
   p1.print("Hello,World!");
  
   Printer<AddDecorator> p2;
   p2.print("Hello,World!");

return 0;
}
========================================
   是不是很像多态?更具体地说是设计模式中的Strategy模式。
   这么做的好处:
   1)比用多态时简单,不需要先构建Strategy对象再传入构造函数,故构造对象的代码简洁。
   2)灵活机动,不需要类阶层,只要实现相关接口就可以召之即来。如果没有实现相关接口会有明显的编译错误提示。
   这么做的缺点——和c++模板的缺点是一样的:
   1)易读性比较不好,调试比较困难
   2)模板只能定义在.h文件中,当工程大了之后,编译时间十分的变态。
  
   结论:有些场合可以让代码较简洁,但应避免在比较底层的地方使用。

评论:

这是一个比较普遍的例子,实现通过传递不同的模板类型参数,实现对不同类的功能的调用,

这就需要一个调用公共接口。这个接口 可以用函数模板来充当,也可以用类模板来充当。

如果用类模板来充当,这个类模板可以是一个任意的类模板,也可以将类继承派生机制用起来,使用具体类的基类作为公共接口。

WTL中就是这么干的。比如窗口类的派生类,在向其基类模板中传递参数时,将自身类名传递给基类的模板参数,这样,就实现在基类这个公共调用接口中 实现对派生类的调用!!!

这种方式优雅地将 继承、派生机制 和模板机制有机结合起来。

C++静态多态(模版模拟多态)的讨论相关推荐

  1. c++中的多态---1(多态概念,静态联编和动态联编,多态原理解析,重载,重写,重定义的对比)

    多态的基本概念 多态是面向对象设计语言数据抽象和继承之外的第三个基本特征 多态性(polymorphism)提供接口与具体实现之间的另一层隔膜,从而将"what"和"ho ...

  2. C++知识点 多态、静态多态、动态多态

    多态 有了继承才有多态的概念,首先说一下继承. 继承的概念是派生类可以调用基类的成员.常举的例子,动物是基类,它拥有所有动物共有的一些特征和方法.它会衍生出猫的类,狗的类,派生出的类除了有动物公共的特 ...

  3. 编写Java程序,以继承和多态思想模拟饲养员喂养不同动物的不同行为

    返回本章节 返回作业目录 需求说明: 以继承和多态思想模拟饲养员喂养不同动物的不同行为 动物园有饲养员和动物,其中动物有老虎.马.猴子.羊.狼等. 饲养员对不同的动物有不同的喂养行为. 实现思路: 以 ...

  4. C++多态——静态多态与动态多态

    多态 : 顾名思义,多态就是多种形态,也就是对不同对象发送同一个消息,不同对象会做出不同的响应. 并且多态分为静态多态和动态多态. 静态多态就是在系统编译期间就可以确定程序执行到这里将要执行哪个函数, ...

  5. 用多态实现模拟电脑-硬盘-U盘-MP3读写数据的功能

    namespace 电脑_移动硬盘_U盘_MP3 {     class Program     {         static void Main(string[] args)         { ...

  6. java静态多态实例,java多态

    市面上对于多态往往有两种说法,一种是多态是通过方法的重载和方法的重写来实现多态,另外一种是父类型的引用指向子类型的对象或者接口类型的引用指向实现接口的类的实例. 其实两种说法都不全面,首先,要从字面上 ...

  7. java 多态_Java面向对象 —— 多态

    前两天已经相继介绍了Java面向对象的三大特性之中的封装.继承,所以今天就介绍Java面向对象的三大特性的最后一项,多态~ 首先讲一下什么是多态,以及多态需要注意的细节 什么是多态:一个对象具备多种形 ...

  8. C++编译时多态和运行时多态

    C++编译时多态和运行时多态 作者:melonstreet 出处:https://www.cnblogs.com/QG-whz/p/5132745.html 本文版权归作者和博客园共有,欢迎转载,但未 ...

  9. c++ 多态 运行时多态和编译时多态_C++学习笔记之多态

    多态是面向对象三大特性之一 多态分为两类: 静态多态:函数重载 和 运算符重载 属于静态多态,复用函数名 动态多态:派生类 和 虚函数 实现运行时多态 静态多态和动态多态的区别: 静态多态的函数地址早 ...

最新文章

  1. 《Python程序设计》题库(2)
  2. CodeForces 567F DP Mausoleum
  3. 统计学习方法(第二版)-学习笔记
  4. python构造callable_Python callable内置函数原理解析
  5. 美国科技投资交易约4.1%来自中国 投资仍然很困难
  6. 充值,提现功能涉及的多线程、队列问题
  7. ofo生死局:拿什么续命?
  8. 回文数c语言 字符串,力扣 009 回文数 C语言
  9. STM32开发环境配置
  10. 资源下载类网站源码 第二版本分享
  11. 2013 年 2 月专访董明珠
  12. python考拉兹猜想_Python练习题 042:Project Euler 014:最长的考拉兹序列
  13. 自定义身份证输入键盘
  14. Unity3d开发MOBA游戏类《王者荣耀》记录(一)
  15. Android远程登录Telnet配置
  16. linux下删除oracle数据库实例
  17. 博图买什么样配置的笔记本_西门子PLC编程软件-博图软件用什么配置的电脑最好?...
  18. 决策树 (Decision Tree) 原理简述及相关算法(ID3,C4.5)
  19. USB设备,鼠标,键盘使用CH372,CH375,进行模拟的历程
  20. ffdshow 源代码分析 3: 位图覆盖滤镜(设置部分Settings)

热门文章

  1. 微服务网关-Gateway-LoadBalancerClient实现负载均衡讲解
  2. 函数的参数-在函数内部使用方法修改可变参数会影响外部实参
  3. 函数的返回值-接收返回元组函数的方式
  4. DSA签名算法 - Java加密与安全
  5. oracle与jdbc连接数据库,JDBC与Oracle数据库连接
  6. _LVM——让Linux磁盘空间的弹性管理
  7. elasticsearch系列五:搜索详解(查询建议介绍、Suggester 介绍)
  8. Nginx 反向代理时获取用户的真实 IP
  9. plsql developer 64位版本
  10. 纽瓦克市政厅电脑遭勒索软件劫持,部分公共服务被迫瘫痪