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

首先看个正常通过虚函数实现多态的常规例子,如下所示,很简单明了无需多言。

 
#include <iostream>
#include <string> class BasicClassic
{
public:
virtual void Print() = 0;
}; class DerivedClassic1 : public BasicClassic
{
public:
DerivedClassic1() {} virtual void Print() {
std::cout << "DerivedClassic1 Print" << std::endl;
}
}; class DerivedClassic2 : public BasicClassic
{
public:
DerivedClassic2() {} virtual void Print() {
std::cout << "DerivedClassic2 Print" << std::endl;
}
};
通过虚函数,在运行时通过虚函数表指针,通过索引找到对应函数,然后进行调用,所以前面我们称之为动态多态或运行时多态。那静态多态又是怎么回事,如何实现呢?答案是模版。如果熟悉COM的同学应该见过框架中很多通过模版来实现多态的实现,看下如下的实现:
 
template<typename Derived>
class Basic
{
public:
inline void Print() {
SelfCast()->Print();
} protected:
inline Derived* SelfCast() {
return static_cast <Derived*>(this);
}
};
class Derived1 : public Basic<Derived1>
{
public:
Derived1() {} inline void Print() {
std::cout << "Derived1 Print" << std::endl;
}
};
class Derived2 : public Basic<Derived2>
{
public :
Derived2() {} inline void Print() {
std::cout << "Derived2 Print" << std::endl;
} static std::string Name() {
return "Derived2 Class" ;
}
};
 
具体使用的代码:
 
Basic<Derived1>* der1 = new Derived1();
der1->Print();
Basic<Derived2>* der2 = new Derived2();
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函数的类似多态特性,如下所示:
 
template <typename Derived>
class Basic
{
public :
Basic() { } inline void Print() {
std::cout << Basic<Derived>::Name() << std::endl;
SelfCast()->Print();
} static std::string Name() {
return Derived::Name();
} protected :
inline Derived* SelfCast() {
return static_cast <Derived*>( this);
}
}; class Derived1 : public Basic<Derived1>
{
public :
Derived1() {} inline void Print() {
std::cout << "Derived1 Print" << std::endl;
} static std::string Name() {
return "Derived1 Class" ;
}
};
 
也就是说针对某些希望定义为static的函数,当你希望能在基类抽象方法中根据当前对象具体类型来使用子类相应static函数时,如上方法可以达成你的目的,这未尝不是一种不错的实现方式。
ok,静态多态就说到这,延伸还有哪些特定的应用,欢迎大家一起讨论。

【C++模版之旅】静态多态的讨论的更多相关文章

  1. 【C++模版之旅】项目中一次活用C++模板(traits)的经历 -新注解

    问题与需求: 请读者先看这篇文章,[C++模版之旅]项目中一次活用C++模板(traits)的经历. 对于此篇文章提出的问题,我给出一个新的思路. talking is cheap,show me t ...

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

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

  3. C++进阶--静态多态

    //############################################################################ /* * 多态 */ // 常见的动态多态 ...

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

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

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

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

  6. c++ 静态多态与动态多态

    多态polymorphism是指具有多种形态的情况,它能根据单一的标记关联不同的行为.多态是面向对象程序设计的基础.在面向对象程序设计中的多态是一种运行时的多态.C++中有两种多态,称为动多态(运行时 ...

  7. 【C++模版之旅】项目中一次活用C++模板(traits)的经历

    曾经曾在一个项目中碰到过一个挺简单的问题,但一时又不能用普通常规的方法去非常好的解决,最后通过C++模板的活用,通过traits相对照较巧妙的攻克了这个问题.本文主要想重现问题发生,若干解决方式的比較 ...

  8. Android逆向之旅---静态方式分析破解视频编辑应用「Vue」水印问题

    一.故事背景 现在很多人都喜欢玩文艺,特别是我身边的UI们,拍照一分钟修图半小时.就是为了能够在朋友圈显得逼格高,不过的确是挺好看的,修图的软件太多了就不多说了,而且一般都没有水印啥的.相比较短视频有 ...

  9. c++ 多态,虚函数、重载函数、模版函数

    c++三大特性:封装.继承.多态.封装使代码模块化,继承扩展已存在的代码,多态的目的是为了接口重用 虚函数实现:虚函数表:指针放到虚函数表 多态:同名函数对应到不同的实现 构造父类指针指向子类的对象 ...

随机推荐

  1. 解决Qt5.7.0 cannot find -lGL

    很久没用Qt了,这次要做一个协议编辑器,在ubuntu 14.04上安装了最新版本的Qt 5.7.0.界面改用扁平化风格,第一感觉还不错.按默认步骤创建了一个gui程序,编译运行,报了一个错:cann ...

  2. (转)iOS keychain API及其封装

    一. Keychain API KeyChain中item的结构为: 1.增加keychain Item OSStatus SecItemAdd (CFDictionaryRef attributes ...

  3. 你需要知道的九大排序算法【Python实现】之冒泡排序

    二.冒泡排序 基本思想:它的思路很有特点循环,两两向后比较.它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数列的工作是重复地进行直到没有再需要交换,也就是说该数 ...

  4. POJ 2175 Evacuation Plan (费用流,负环,消圈法,SPFA)

    http://poj.org/problem?id=2175 Evacuation Plan Time Limit: 1000MS   Memory Limit: 65536K Total Submi ...

  5. Spring Data Redis—Pub/Sub(附Web项目源码)

    一.发布和订阅机制 当一个客户端通过 PUBLISH 命令向订阅者发送信息的时候,我们称这个客户端为发布者(publisher). 而当一个客户端使用 SUBSCRIBE 或者 PSUBSCRIBE ...

  6. 秀才每天一篇之—SEO是什么?

    [导读]SEO是什么?秀才搁笔七天以来.遇到非常多事情.站点被K ,排名掉了. 却找不出原因,開始迷茫了.以至于没有心情来照应这个博客,不好意思了,各位.在这七天以来.秀才一直在思考,SEO究竟应该怎 ...

  7. python模块基础之OS模块

    OS模块简单的来说它是一个Python的系统编程的操作模块,可以处理文件和目录这些我们日常手动需要做的操作. 可以查看OS模块的帮助文档: >>> import os #导入os模块 ...

  8. django: db - admin

    本讲演示简单使用 Django Admin 功能. 一,修改 settings.py,添加 admin 应用: INSTALLED_APPS = ( 'django.contrib.auth', 'd ...

  9. HTML5 Canvas显示本地图片实例1、Canvas预览图片实例1

    1.前台代码: <input id="fileOne" type="file" /> <canvas id="canvasOne&q ...

  10. Intent 能传递的数据类型

    1. Serializable,将对象序列化成二进制数据传递 2. charsequence: 主要用来传递String,char等 3. parcelable: 这个android提供的一种新的类型 ...