CRTP可以使得类具有类似virtual function的效果,同时还没有virtual function的调用开销,因为virtual function调用需要通过vptr来找到vtbl进而找到真正的函数指针进行调用,同时对象size相比使用virtual function也会减小,但是CRTP也有明显的缺点,最直接的就是代码可读性降低(模板代码的通病),还有模板实例化之后的code size有可能更大
普通多态
class Base {
public:
virtual void foo() = 0;
virtual void bar() = 0;
};
class Derived1 : public Base {
public:
virtual void foo() override final { cout << "Derived1 foo" << endl; }
virtual void bar() override final { cout << "Derived1 bar" << endl; }
};
class Derived2 : public Base {
public:
virtual void foo() override final { cout << "Derived2 foo" << endl; }
virtual void bar() override final { cout << "Derived2 bar" << endl; }
};
静态多态
可以在基类中使用static_cast将this指针转换为子类的指针然后调用该子类的方法。
// 静态多态===============================================================================================
template<typename T>
class Base {
public:
void foo() { static_cast<T *>(this)->internal_foo(); }
void bar() { static_cast<T *>(this)->internal_bar(); }
};
class Derived1 : public Base<Derived1> {
public:
void internal_foo() { cout << "Derived1 foo" << endl; }
void internal_bar() { cout << "Derived1 bar" << endl; }
};
class Derived2 : public Base<Derived2> {
public:
void internal_foo() { cout << "Derived2 foo" << endl; }
void internal_bar() { cout << "Derived2 bar" << endl; }
};
template <typename T>
void foo(Base<T> &obj) { obj.foo(); }
template <typename T>
void bar(Base<T> &obj) { obj.bar(); }
int main(int argc, char** argv) {
Derived1 d1;
Derived2 d2;
foo(d1);
foo(d2);
bar(d1);
bar(d2);
return 0;
}
利用main函数进行测试得到:
int main(int argc, char** argv) {
Derived1 d1;
Derived2 d2;
foo(d1);
foo(d2);
bar(d1);
bar(d2);
cout << "==================================\n";
t::Derived1 t1;
t::Derived2 t2;
t::Base *p = &t1;
t::Base *q = &t2;
p->foo();
q->foo();
p->bar();
q->bar();
return 0;
}
// 其实这个也类似于代码复用
template<typename T>
class Base {
public:
static int getObjCnt() { return cnt; }
protected:
static int cnt ;
};
// 对cnt进行初始化,static修饰的成员不能再类内初始化
// 非const的static变量如果在当前文件初始化的话, 编译器会将它翻译成一个强符号.如果在类中初始化, 当这个类作为头文件的一员被引入到多个文件后, 就会产生多个名字相同的强符号, 链接时就会出现重复定义的错误.
template <typename T>
int Base<T>::cnt = 0;
class Derived1 : public Base<Derived1> {
public:
Derived1() { cnt++; }
};
class Derived2 : public Base<Derived2> {
public:
Derived2() { cnt++; }
};
用main函数进行测试
int main(int argc, char** argv) {
Derived1 d11, d12, d13;
Derived2 d21, d22;
cout << "Derived1::getObjCnt() = " << Derived1::getObjCnt() << endl;
cout << "Derived2::getObjCnt() = " << Derived2::getObjCnt() << endl;
return 0;
}
结果如下
参考链接
如果可以使用静态多态来替换动态多态的话,CRTP确实能提升程序的性能,但说到底CRTP有可能省下的只是函数调用的开销,如果说函数体是计算密集型的,这时候函数调用的开销几乎可以忽略不计,那么这时候CRTP就不一定是最好的选择,在实际的场景中,具体使用哪种方案还是要具体情况具体分析。
如果不是对性能有极致追求,谨慎使用!