早些年面试时,经常会背些八股文。面试官问你多态的概念,立马就把网上看的一些知识点背诵出来。多态分为静态多态和动态多态,所谓静态多态就是函数重载 ,动态多态就是virtual之类的。
几年过去了,无意之中,在一本书上看到CRTP的概念,奇异递归模板,这个属于既要又要类型的高科技,就是既要多态,又不要virtual的性能开销,因为virtual多了一次通过虚函数表查找的开销,再就是虚函数对象多了虚函数指针。
那这个还背不背呢,背吧,平时基本没涉及到,不背吧,好像这个知识点挺高大上的。反正了这些年就一直有个模糊的印象,但是看到模板就头大 ,也不敢了解到底是怎么做到既要又要的。
今天闲来无事,又在一本书上看到了这个概念,这次还逃避就说不出去了。看了几分钟,这手段虽然自己没有扎实的技术功底,提不出来,但是别人弄出来了,自己看懂还是没问题的。
我们就来慢慢揭开CRTP的神秘面纱吧。
首先,不用virtual怎么实现多态呢,可能你想不到,但是一说答案,你肯定会说就这啊,我也知道,就是把基类指针强转为派生类指针,你想调用哪个类的函数,就强转为哪个派生类。这好像是个正确的废话,但是这的确是迈出了第一步啊。
你肯定要说,你代码就是这么写的,别人给你个基类指针,你就直接强转为派生类指针,你也不担心崩了,当然担心啊,这就是我们接下来要解决的问题。
要想不玩崩,那就必须保证传过来的基类指针,的确是指向目标派生类指针的,而且给你基类指针,你还不能乱转,就只能强转为正确的本身所代表的派生类指针。好像有点绕,把我整不会了。
要保证不能乱转,你可能会想到模板,你的类型T由外面传进来,然后强转为T类型的指针,这就保证了只能强转为正确的本身所代表的派生类指针。
最后,也是关键的地方,还能这么玩,我是想不到,让派生类继承模板基类,模板参数T指定为派生类,这样就闭环了,说了这么多,还是上代码吧。`在这里插入代码片`
template <class T>
class Control
{
public:
void draw()
{
static_cast<T*>(this)->paint();
}
}
class Button : public Control<Button>
{
public:
void paint()
{
}
}
调用的时候
template <class T>
void draw_control(Contriol<T>& c)
{
c.draw();
}
Button b;
draw_control(b);
是时候总结了,以前弱小的时候,可以逃避,长大了,得一个个去攻破难点啊。