在C++编程中,有许多精妙的设计模式和技巧,CRTP(Curiously Recurring Template Pattern,奇异递归模板模式)便是其中之一,作为一种简洁而强大的模式,被广泛用于提高代码的灵活性和性能。
基本思想
CRTP利用模板元编程的特性,通过模板继承的方式实现递归结构。CRTP的基本思想:基类模板以派生类作为模板参数,在基类接口中将this指针强转为派生类指针,调用派生类中的方法,从而实现了一种静态多态性。
如下的示例代码有助于理解CRTP的工作原理:
// 基类模板
template <typename Derived>
class CBase {
public:
void commonMethod() {
static_cast<Derived*>(this)->implementation();
}
void anotherCommonMethod() {
std::cout << "Another common method in Base." << std::endl;
}
};
// 派生类1
class Derived1 : public CBase<Derived1> {
public:
void implementation() {
std::cout << "Implementation in Derived1." << std::endl;
}
};
// 派生类2
class Derived2 : public CBase<Derived2> {
public:
void implementation() {
std::cout << "Implementation in Derived2." << std::endl;
}
};
void test_CRTP()
{
Derived1 d1;
d1.commonMethod();
d1.anotherCommonMethod();
Derived2 d2;
d2.commonMethod();
d2.anotherCommonMethod();
}
由如上代码可知,CBase模板类作为一个接口,定义了一个commonMethod方法,它通过CRTP的方式调用派生类中的方法implementation。派生类Derived1和Derived2都继承自CBase,并实现了implementation方法。通过CRTP,我们可以在基类中调用派生类的方法,而不需要使用虚函数的运行时开销。
扩展
CRTP在C++标准库中有广泛应用,模板类std::enable_shared_from_this便是其中之一,应用代码如下:
class MyClass : public std::enable_shared_from_this<MyClass> {
public:
void foo()
{
// 在成员函数中调用 shared_from_this() 获取指向自身的 std::shared_ptr
std::shared_ptr<MyClass> ptr = shared_from_this();
std::cout << "SharedPtr count: " << ptr.use_count() << std::endl;
}
};
int using_enable()
{
std::shared_ptr<MyClass> objPtr = std::make_shared<MyClass>();
// 在 MyClass 的成员函数中调用 shared_from_this() 方法
objPtr->foo();
return 0;
}
自定义类继承自模板类std::enable_shared_from_this,便可以在自定义类中使用模板类中的shared_from_this() 方法。注意需要自定义本身调用了shared_ptr的构造函数后,方可调用shared_from_this() 方法。
这是因为shared_from_this()使用模板类内持有的弱引用指针进行转换得到自身的共享指针shared_ptr。只有调用shared_ptr的构造函数且自定义类继承自std::enable_shared_from_this时才会给弱指针赋值。如果弱引用指针未被赋值,空指针强转会触发异常。
回归CRTP,自定义类需要将自身作为模板参数传递给 std::enable_shared_from_this,在派生类中才可以使用 std::enable_shared_from_this 提供的 shared_from_this() 方法。
如果将非自定义类作为模板参数传递给 std::enable_shared_from_this会出现编译错误,示例如下
class A
{
public:
A()=default;
~A()=default;
};
class MyClass :
public std::enable_shared_from_this<A>
{
public:
void foo() {
//error, error C2440: “初始化”: 无法从“std::shared_ptr<A>”转换为“std::shared_ptr<MyClass>”
//std::shared_ptr<MyClass> ptr = shared_from_this();
//std::cout << "SharedPtr count: " << ptr.use_count() << std::endl;
}
};
CRTP的应用场景
CRTP通常用于需要在编译期解决多态性问题的情况下,以提高代码性能和灵活性。一些常见的应用场景包括:
结论
CRTP是一种强大的C++编程技巧,通过模板元编程实现了静态多态性和灵活性。尽管在一般情况下不太容易理解,但一旦掌握其原理和应用场景,便能发挥其巨大的作用。在实际项目中,我们可以将CRTP应用于各种场景,从而提高代码的性能和可维护性。