C++怎么将多态用到模板类中?

它通过继承和模板的联合应用,实现了一种"看似"继承自己的语法。这种编程的技法,无论是在STL还是Boost之中都被大量使用。像它的名字一样,看起来很Curiously。笔者在进行数据库源码学习和开发时,发现无论是Clickhouse还是Doris中也同样大量使用了这种编程技巧来简化代码和提高性能。
接下来,用一杯咖啡的时间,来和大家详细聊聊这种模板的黑魔法

当我们有类需要被智能指针share_ptr管理,且需要通过类的成员函数里需要把当前类对象包装为智能指针传递出一个指向自身的share_ptr时。在这种情况下类就需要通过继承enable_shared_from_this,通过父类的成员函数shared_from_this来获取指向该类的智能指针。

我们来看看具体的代码实现逻辑:

这里我们可以看到,Good类继承了std::enable_shared_from_this,并且自己是作为模板参数传递给父类的。这就给让代码看起来有些"唬人",看起来像是继承自己一样。但其实呢?这里只是用到了模板派生,让父类能够在编译器感知到子类的模板存在,二者不是真正意义上的继承关系。

这里只分析下面两个问题:

    1. 答:因为原本的this指针就是被shared_ptr管理的,通过getprt函数构造的新的智能指针和和原本管理this指针的的shared_ptr并不互相感知。这会导致指向Bad的this指针被二次释放!!!

std::enable_shared_from_this的实现由于有些复杂,受限于篇幅。笔者就不展开来分析它具体是怎么样实现的了。它的能够规避上述问题的原因如下:

enable_shared_from_this的实现逻辑不是本篇的重点,感兴趣的朋友可以自行看看STL的源码更为彻底的整明白它的实现。

我们重点来看看,这个CRTP在上文的enable_shared_from_this之中起到了怎么样的作用。从1.1的代码之中我们可以看到。它核心的作用是利用子类的信息来生成代码,我们来具体看看对应的代码实现

  1. 这里通过子类的模板信息,在父类之中派生出一个指向自身的weak_ptr。

通过这两个核心的派生逻辑,大体上就完成了enable_shared_from_this的骨架构建了。

所以,其实CRTP只不过是表面上看起来有些"唬人"。它的核心作用就是只有一条:是利用子类的信息来生成代码。

Operator,我们也可以抛弃Boost啦,妈妈再也不用担心我写不好重载操作符了~~)

在上一节之中,我们了解了CRTP的实现。当然这种“奇技淫巧”并不是用来装逼的。所以本节笔者就结合自己本身的实践,来描述一下CRTP应该如何在实际的编码场景之中使用,以及能够解决一些什么样的问题。

在Clickhouse之中,大量使用了CRTP来实现静态多态的形式来减少虚函数的调度开销。

数据以一个个tuple形式在操作符之间传递,而由于操作符之间不断交互,导致了大量的虚函数调用开销,影响执行效率。因为虚函数的调用需要通过指针查找虚函数表来进行调用,同时类的对象因为不需要存储虚函数指针,也会带来一部分存储的开销。而通过CRTP,恰恰就能通过静态多态的方式,规避上述问题。

  • Clickhouse的聚合函数继承了IAggregateFunctionHelper接口。它就是一个典型的CRTP的使用,利用静态多态的方式。将虚函数的调用转换为函数指针的调用,这个在实际聚合函数的实现过程之中能够大大提高计算的效率。我们来看看具体的代码:

通过这种CRTP的巧妙方式降低了上面提到的虚函数开销。

在Clickhouse的代码注释之中提到,通过CRTP的方式,能够有12%的性能提升。可见这种静态多态的方式对于OLAP的系统的性能的确是有显著的提升的。

说完了Clickhouse,当然得提一嘴自家的Doris。Doris之中应用了CRTP来实现颠倒继承的目的。

Inheritance),顾名思义就是通过父类向子类添加功能。因为它的效果与普通继承父到子的逻辑是相反的。第一节的enable_shared_from_this就是利用了颠倒继承来实现所需要的功能的。接下来,我们来看看Doris的代码吧:

  • Doris实现了一个线程安全的Queue结构,它的内部实现了一个Node类。它的nextprev函数就是利用了颠倒继承与reinterpret_cast<T*>的强制类型转换,让父类获取了能够返回子类指针的能力,从而让子类再通过继承拥有了对应的能力。

通过CRTP实现颠倒继承的方式,能够大大减少我们需要额外编写的代码量,简化我们的代码结构和减少coding工作。但是带来的缺点也很明显,这种通过模板派生的形式生成的代码与宏定义一般,相对来说难以理解,不易调试。所以,舍得之间,大家需要自己选择。

看到这里,想必大家手里的咖啡也喝完了哈。本篇介绍了一个模板使用的黑魔法:CRTP。它在高性能数据库,金融系统领域作为一种编程技法被大量使用。但是由于其怪异的语法,坦率来说对初学者并不友好。
管中窥豹,我们可以通过CRTP看到C++模板的强大魅力。无论是在代码简化,性能提升方面都值得我们继续深入思考学习,也欢迎大家多多讨论,指教。


1.C++中的虚函数C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议。 对C++ 了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)和一个指向虚函数表的指针(vptr)来实现的。虚函数表,简称为vtbl,虚函数表表对实现多态起着至关重要

我要回帖

更多关于 如何将当前ppt设置为模板 的文章

 

随机推荐