虚函数是C++实现多态的工具,在运行时根据虚表决定调用合适的函数。这被称作动态分发。虚函数很好的实现了多态的要求,但是在运行时引入了一些开销,包括:

  1. 对每一个虚函数的调用都需要额外的指针寻址
  2. 虚函数通常不能被inline,当虚函数都是小函数时会有比较大的性能损失
  3. 每个对象都需要有一个额外的指针指向虚表

所以如果是一个对性能要求非常严格的场合,我们就需要用别的方式来实现分发,这就是今天这篇博客的主角CRTP

CRTP通过模板实现了静态分发,会带来很多性能的好处。可以参见The cost of dynamic (virtual calls) vs. static (CRTP) dispatch in C++看一下性能比较。

 

下面简单介绍一下怎么实现CRTP。

 

首先看我们的父类:

 1
2
3
4
5
6
7
8
9
10
11
12
13
template<typename Derived>  class Parent
{
public:
void SayHi()
{
static_cast<Derived*>(this)->SayHiImpl();
}
private:
void SayHiImpl() // no need if no need the default implementation
{
cout << "hi, i'm default!" << endl;
}
};

它是一个模板类,它有一个需要接口函数是SayHi。它有一个默认实现在SayHiImpl中。

 

再来看看它的子类:

 1
2
3
4
5
6
7
8
9
10
11
12
class ChildA :public Parent<ChildA>
{
public:
void SayHiImpl()
{
cout << "hi, i'm child A!" << endl;
}
}; class ChildB :public Parent<ChildB>
{
};

我们可以看到ChildAChildB继承自这个模板类,同时ChildA有自己的实现。

 

在写一个普通的用虚函数实现分发的类:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class ParentB
{
public:
void virtual SayHi()
{
cout << "hi, i'm default!" << endl;
}
}; class ChildC : public ParentB
{
public:
void SayHi()
{
cout << "hi, i'm ChildC!" << endl;
}
}; class ChildD : public ParentB
{
};

 

然后是调用这两个父类的函数:

1
2
3
4
5
6
7
8
9
template<typename Derived> void CRTP(Parent<Derived>& p)
{
p.SayHi();
} void Dynamic(ParentB& p)
{
p.SayHi();
}

 

再来看看main函数:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int _tmain(int argc, TCHAR* argv[])
{ ChildA a;
CRTP(a);
cout << "size of ChildA: " << sizeof(a) << endl; ChildB b;
CRTP(b);
cout << "size of ChildB: " << sizeof(b) << endl; ChildC c;
Dynamic(c);
cout << "size of ChildC: " << sizeof(c) << endl; ChildD d;
Dynamic(d);
cout << "size of ChildD: " << sizeof(d) << endl; return 0;
}

 

如果运行这个程序,可以看到如下的输出,可以看到CRTP可以实现和虚函数一样的功能,但是内存大小会有很大优势,关于对象内存可以参见我之前的博客怎么看C++对象的内存结构 和 怎么解密C++的name Mangling

 

1
2
3
4
5
6
7
8
9
hi, i'm child A!
size of ChildA: 1
hi, i'm default!
size of ChildB: 1
hi, i'm ChildC!
size of ChildC: 4
hi, i'm default!
size of ChildD: 4
Press any key to continue . . .

 

完整代码参见gist

C++的静态分发(CRTP)和动态分发(虚函数多态)的比较的更多相关文章

  1. java的静态代理、jdk动态代理和cglib动态代理

    Java的代理就是客户端不再直接和委托类打交道,而是通过一个中间层来访问,这个中间层就是代理.使用代理有两个好处,一是可以隐藏委托类的实现:二是可以实现客户与委托类之间的解耦,在不修改委托类代码的情况 ...

  2. 《CMake实践》笔记三:构建静态库(.a) 与 动态库(.so) 及 如何使用外部共享库和头文件

    <CMake实践>笔记一:PROJECT/MESSAGE/ADD_EXECUTABLE <CMake实践>笔记二:INSTALL/CMAKE_INSTALL_PREFIX &l ...

  3. 浅谈在静态页面上使用动态参数,会造成spider多次和重复抓取的解决方案

    原因: 早期由于搜索引擎蜘蛛的不完善,蜘蛛在爬行动态的url的时候很容易由于网站程序的不合理等原因造成蜘蛛迷路死循环. 所以蜘蛛为了避免之前现象就不读取动态的url,特别是带?的url 解决方案: 1 ...

  4. 在Linux中创建静态库.a和动态库.so

    转自:http://www.cnblogs.com/laojie4321/archive/2012/03/28/2421056.html 在Linux中创建静态库.a和动态库.so 我们通常把一些公用 ...

  5. 动态库DLL加载方式-静态加载和动态加载

    静态加载: 如果你有a.dll和a.lib,两个文件都有的话可以用静态加载的方式: message函数的声明你应该知道吧,把它的声明和下面的语句写到一个头文件中 #pragma comment(lib ...

  6. WPF中静态引用资源与动态引用资源的区别

    WPF中静态引用资源与动态引用资源的区别   WPF中引用资源分为静态引用与动态引用,两者的区别在哪里呢?我们通过一个小的例子来理解. 点击“Update”按钮,第2个按钮的文字会变成“更上一层楼”, ...

  7. 解决在静态页面上使用动态参数,造成spider多次和重复抓取的问题

    我们在使用百度统计中的SEO建议检查网站时,总是发现“静态页参数”一项被扣了18分,扣分原因是“在静态页面上使用动态参数,会造成spider多次和重复抓取”.一般来说静态页面上使用少量的动态参数的话并 ...

  8. C++的静态联编和动态联编

    联编的概念 联编是指一个计算机程序自身彼此关联的过程,在这个联编过程中,需要确定程序中的操作调用(函数调用)与执行该操作(函数)的代码段之间的映射关系. 意思就是这个函数的实现有多种,联编就是把调用和 ...

  9. Linux 静态链接库和动态连接库

    (0)文件夹 VMware 下安装Ubuntu的吐血经历 零基础学习Shell编程 Linux下的makefile的妙用 Linux调试神器 -- gdb 十分钟学会Python的基本类型 Linux ...

随机推荐

  1. postgresql info

    DROP TYPE IF EXISTS info CASCADE;CREATE TYPE info AS (state int4,operator varchar,time TIMESTAMP(0) ...

  2. BZOJ1861[ZJOI2006]Book书架

    Description 小T有一个很大的书柜.这个书柜的构造有些独特,即书柜里的书是从上至下堆放成一列.她用1到n的正整数给每本书都编了号. 小T在看书的时候,每次取出一本书,看完后放回书柜然后再拿下 ...

  3. BZOJ 1717: [Usaco2006 Dec]Milk Patterns 产奶的模式

    Description 农夫John发现他的奶牛产奶的质量一直在变动.经过细致的调查,他发现:虽然他不能预见明天产奶的质量,但连续的若干天的质量有很多重叠.我们称之为一个"模式". ...

  4. hdu 1269

    强连通分量题,用tarjin算法: 这是一道很简单的tarjin算法题,基本上就是套模板: 贴代码: #include<cstdio> #include<vector> #in ...

  5. Codeforces Round #197 (Div. 2) : A

    水题一个: 直接贴代码: #include<cstdio> #include<algorithm> #include<cstring> using namespac ...

  6. The APR based Apache Tomcat Native library

    Tomcat启动的时候出现下面这样的提示: 2015-11-06 14:24:12 org.apache.catalina.core.AprLifecycleListener init 信息: The ...

  7. 李洪强iOS开发Swift篇—05_元组类型

    李洪强iOS开发Swift篇—05_元组类型 一.元组类型介绍 1.什么是元组类型 元组类型由 N个 任意类型的数据组成(N >= 0),组成元组类型的数据可以称为“元素” 示例: let po ...

  8. C++开发与Windows API

    Windows API 向 C++ 开发人员提出了一项挑战. 组成 API 的众多库大都表现为 C 语言风格的函数和句柄或是 COM 风格的接口. 这些用起来都不太方便,需要进行一定的封装或间接操作. ...

  9. 【Xamarin开发 Android 系列 5】 Xamarin 的破解

    原文:[Xamarin开发 Android 系列 5] Xamarin 的破解 有关这个话题,十分敏感,公司开发还是支持下商业版权吧,毕竟一帮猴子辛辛苦苦没日没夜的干活,不说开宝马奔驰,吃饭还是必须的 ...

  10. 转:三十二、Java图形化界面设计——布局管理器之CardLayout(卡片布局)

    转:http://blog.csdn.net/liujun13579/article/details/7773945 卡片布局能够让多个组件共享同一个显示空间,共享空间的组件之间的关系就像一叠牌,组件 ...