看到这个题目你可能会觉得奇怪,CRTP是什么呢?CRTP是The Curiously Recurring Template Pattern的缩写.
我们先来看一个简单的例子:
template <typename Derived>
class CuriousBase {
…
};
class Curious : public CuriousBase<Curious> {
…
};//摘自<<C++ Templates: The Complete Guide>>
//16.3 The Curiously Recurring Template Pattern (CRTP)
如果是第一次看到,你可能会觉得奇怪(我也想了半天),这里不会形成递归吗?这里有两个地方可能会产生递归,首先是声明,其次是实现的时候.比如说下面的代码:
template <typename Derived>
class CuriousBase
{
public:
CuriousBase()
{
new Derived;
}
};
class Curious : public CuriousBase<Curious>
{
public:
Curious()
{
}
};
如果你的程序中加入了这样的代码,恭喜你了,你中了我们的大奖--崩溃;
你可能会问,即使我们能避免这样的递归,可是这样的结构有什么用呢?
我们先假设有种情况,比如说你的项目里面可能有10000多个类,现在要求你每个类都要实现print()函数,负责将类型的大小输出到std::cout上面,你们的老板还发话了,用函数实现,因为将来这个操作可能需要变更,最可气的是他规定说最好是不要用全局函数.
第一个念头是继承一个有函数print()的接口
class IAblePrint
{
public:
void print()
{
cout<<sizeof(*this)<<endl;
}
};
可惜这样做的结果不是我们需要的,那么我们设这个函数成为虚函数,然后每个类实现自己的?老天,那里有10000多个类等着我们呢.虽然全局函数是个不错的解决方案,但是那个可恶的老板!
直觉告诉我们,这些成员函数一定是要实现的,可是有没有办法让编译器来替我们完成这些无聊的工作呢?
下面就该CRTP登场了
template<typename T>
class AblePrintSize
{
public:
void print()
{
cout<<sizeof(T)<<endl;
}
};
class Test1:public AblePrintSize<Test1>
{
int t;
};
class Test2:public AblePrintSize<Test2>
{
int t[2];
};
int main()
{
Test1 hu1;
Test2 hu2;
hu1.print();
hu2.print();
}
这里我们只是自己多写了一个template class就达到了我们的目标.上面的例子可能举的不是很好,下面有个比较好的例子(摘自<<C++ Templates: The Complete Guide>>
),大家可以参考参考
#include <stddef.h>
template <typename CountedType>
class ObjectCounter {
private:
static size_t count; // number of existing objects
protected:
// default constructor
ObjectCounter() {
++ObjectCounter<CountedType>::count;
}
// copy constructor
ObjectCounter (ObjectCounter<CountedType> const&) {
++ObjectCounter<CountedType>::count;
}
// destructor
~ObjectCounter() {
--ObjectCounter<CountedType>::count;
}
public:
// return number of existing objects:
static size_t live() {
return ObjectCounter<CountedType>::count;
}
};
// initialize counter with zero
template <typename CountedType>
size_t ObjectCounter<CountedType>::count = 0;
/*If we want to count the number of live (that is, not yet destroyed) objects for a certain class type, it suffices to derive the class from the ObjectCounter template. For example, we can define and use a counted string class along the following lines:*/
// inherit/testcounter.cpp
#include "objectcounter.hpp"
#include <iostream>
template <typename CharT>
class MyString : public ObjectCounter<MyString<CharT> > {
…
};
int main()
{
MyString<char> s1, s2;
MyString<wchar_t> ws;
std::cout << "number of MyString<char>: "
<< MyString<char>::live() << std::endl;
std::cout << "number of MyString<wchar_t>: "
<< ws.live() << std::endl;
}