目录
RAII(Resource Acquisition Is Initialization)
CRTP(Curiously Recurring Template Pattern)
SFINAE(Substitution Failure Is Not An Error)
RAII(Resource Acquisition Is Initialization)
即资源获取就是初始化,是一种管理资源的机制。
如管理new操作符的资源:
class A
{
public:
int *p;
A(int n)
{
p = new int[n];
}
~A()
{
delete p;
}
};
int main()
{
A a(10);
// a.p
return 0;
}
只写了一个简单的示例。
一个经典的案例:lock_guard类,源代码如下
// CLASS TEMPLATE lock_guard
template<class _Mutex>
class lock_guard
{ // class with destructor that unlocks a mutex
public:
using mutex_type = _Mutex;
explicit lock_guard(_Mutex& _Mtx)
: _MyMutex(_Mtx)
{ // construct and lock
_MyMutex.lock();
}
lock_guard(_Mutex& _Mtx, adopt_lock_t)
: _MyMutex(_Mtx)
{ // construct but don't lock
}
~lock_guard() noexcept
{ // unlock
_MyMutex.unlock();
}
lock_guard(const lock_guard&) = delete;
lock_guard& operator=(const lock_guard&) = delete;
private:
_Mutex& _MyMutex;
};
实例化的时候加锁,生命周期结束的时候自动解锁。
ScopeGuard
这个方法是定义一个ScopeGuard模板类,用于管理临时使用的资源。
把临时资源的释放操作写成lambda表达式,申请ScopeGuard实例存放这个表达式,实例生命周期结束时自动执行。
代码:
template <typename Lambda>
class ScopeGuard
{
public:
ScopeGuard(Lambda &&lam): lambda (lam) {
}
~ScopeGuard() {
lambda();
}
private:
Lambda lambda;
};
template <typename T>
ScopeGuard<T> GetScopeGuard(T &&lambda)
{
return ScopeGuard<T>(move(lambda));
}
int main()
{
int* p = new int[100];
GetScopeGuard([&] {delete[] p; });
return 0;
}
NVI(Non-Virtual Interface)
NVI是用非虚函数调用虚函数,把非虚函数作为接口。
class A
{
public:
void show()
{
cout << "A\n";
print();
}
virtual void print() {};
};
class B :public A
{
public:
void print()
{
cout << "x=" << x;
}
private:
int x;
};
int main()
{
B b;
b.show();
return 0;
}
CRTP(Curiously Recurring Template Pattern)
奇异的递归模板模式,指的是子类继承一个模板类,模板的特化类型是子类本身。
最简代码:
#include<iostream>
using namespace std;
template<typename T>
class Base
{
public:
void virtualFunc()
{
static_cast<T*>(this)->realFunc();
}
};
class Chird :public Base<Chird>
{
public:
void realFunc()
{
cout << "real func";
}
};
int main()
{
Chird().virtualFunc();
return 0;
}
这个简单的例子就能看出CRTP的写法,也能看出,其实本质上静态多态。
CRTP的好处是,静态多态比继承虚函数的动态多态要快。
应用:
CRTP单例模板
PIMPL(Private Implementation)
通过一个私有的成员指针,将指针所指向的类的内部实现全部隐藏。
普通代码:
class A
{
public:
int f(int n)
{
return f1(n) + n + f2(n);
}
private:
int f1(int n)
{
return n * n * n;
}
int f2(int n)
{
return n > 0 ? 1 : -1;
}
};
PIMPL代码:
class B;
class A
{
public:
A()
{
opt = new B();
}
int f(int n)
{
return opt->f1(n) + n + opt->f2(n);
}
private:
B* opt;
};
class B
{
public:
int f1(int n)
{
return n * n * n;
}
int f2(int n)
{
return n > 0 ? 1 : -1;
}
};
SFINAE(Substitution Failure Is Not An Error)
SFINAE是指泛型函数在编译的时候,会尝试生成所有实例,如果生成失败也不会报错,直到最后确定重载版本的时候如果没有正确版本才会报错。
template <typename T>
void print(T x)
{
cout << x;
}
void print(vector<int>x)
{
cout << x[0];
}
int main()
{
vector<int>x = { 1,2,3 };
print(x);
return 0;
}
在这个例子中,泛型函数print首先会生成实例,但是里面会编译失败,继续编译,普通函数print可以满足条件,最终选用它而忽略泛型实例。
应用:通过重载版本判断一个参数类型是不是类
class A
{
//
};
class IsClass
{
public:
template<typename C> static char f(int C::*);
template<typename C> static int f(...);
};
int main()
{
bool flag1 = sizeof(IsClass::f<A>(0)) == 1; // true
bool flag2 = sizeof(IsClass::f<int>(0)) == 1; // false
return 0;
}
Mixin
Mixin就是模板类继承模板参数类。
代码:
template<class B>
class A:public B
{
public:
void print()
{
cout << "the name is ";
B::print();
}
};
class B
{
public:
void print()
{
cout << "B";
}
};
int main()
{
A<B> a;
a.print();
return 0;
}