如何做网站卖连接企业如何进行网站推广
一、奇异的递归模板模式范例
奇异的递归模板模式 ( C u r i o u s l y R e c u r r i n g T e m p l a t e P a t t e r n ) (Curiously \ Recurring \ Template \ Pattern) (Curiously Recurring Template Pattern)不是一种新技术,而是一种模板编程中使用的编程手法:把派生类作为基类的模板参数。
下面是一个简单范例:
//基类
template<typename T>
class Base {//...
};//派生类1
class Derived1 : public Base<Derived1> { //派生类继承基类的派生类参数的泛型
public:void myfunc() {std::cout << "Derived1::myfunc()执行了!\n";}};//派生类2(模板)
template<typename T>
class Derived2 : public Base<Derived2<T>> {};
这样的继承方式在模板与泛型编程中很常见。
以下介绍一下基本的几个用法。
二、在基类中使用派生类对象
一般情况下,我们可以通过 s t d : : s t a t i c _ c a s t < T > std::static\_cast<T> std::static_cast<T>静态类型转换将基类对象转换为派生类对象,从而在基类对象内使用派生类对象的接口。
如下代码所示:
template<typename T>
class Base {private:Base() {}friend T; //只有在类内实例化Base<T>的T是友元public:void asDerived() {T& derived = static_cast<T&>(*this); //将基类转为派生类derived.myfunc();}
};//派生类1
class Derived1 : public Base<Derived1> { //派生类继承基类的派生类参数的泛型
public:void myfunc() {std::cout << "Derived1::myfunc()执行了!\n";}};
为了防止继承时出错,我们在基类内增加一个友元类 T T T,只对相同派生类的类模板开放构造函数。
使用友元可以避免下面的错误:
相应的友元模板知识点可以参考:友元类模板,这里就不赘述了。
注意这里的写法:
当然,这个派生类也可以是一个模板:
//派生类2(模板)
template<typename T>
class Derived2 : public Base<Derived2<T>> {
public:void myfunc() {std::cout << "Derived2<T>::myfunc()执行了!\n";}
};
同时,我们在基类增加友元声明:
template<typename U>
friend class Derived2; // 允许所有 Derived2 的实例成为友元
这样我们实例化出的派生类都支持基类的私有访问了,调用下方测试代码:
void Test1() {Derived1 myd1;myd1.asDerived();Derived2<int> myd2;myd2.asDerived();
}
这样的运行结果是,我们在基类调用了派生类的方法,也就是可以看作是派生类为基类提供了接口,这与一般的继承调用不同(基类给派生类提供接口)
运行结果如下:
三、基于减少派生类代码量的考虑
通常,如果我们需要在派生类内增加功能,我们需要不断的补充代码,而这里的做法是,我们把一部分代码放入基类实现,让派生类内的代码相对简洁一些。
下面是一个例子:
//基于减少派生类中代码量的考虑template<typename T>
struct shape {//友元实现运算符重载friend bool operator==(const shape<T>& obj1, const shape<T>& obj2) {const T& objtmp1 = static_cast<const T&>(obj1);const T& objtmp2 = static_cast<const T&>(obj2);if (!(objtmp1 < objtmp2) && !(objtmp2 < objtmp1)) return true;return false;}
};struct square :public shape<square> {int sidelength;square(int length) :sidelength(length) {}};//全局实现运算符重载
bool operator<(square const& obj1, square const& obj2) {return obj1.sidelength < obj2.sidelength;
}
我们需要在 s q u a r e square square派生类里面增加一个运算符重载,我们可以把这一个操作放入基类中来实现,或者放入全局来实现。
这里详细说一下在基类实现:
1.我们需要将运算符设置为友元的,这样我们就能在派生类内访问到基类的运算符了。这样设置的友元是全局的,这在之前的友元章节有详细说明。
2.然后我们需要使用 s t d : : s t a t i c _ c a s t < T > std::static\_cast<T> std::static_cast<T>对传入的基类对象转为相应的派生类对象,最后进行比较即可。
调用测试代码:
void Test2() {square obj1(15), obj2(20), obj3(20);if (obj1 == obj2) {std::cout << "obj1 和obj2相等\n";}else {std::cout << "obj1 和obj2不相等\n";}if (obj2 == obj3) {std::cout << "obj2 和obj3相等\n";}else {std::cout << "obj2 和obj3不相等\n";}
这样,我们在派生类中无需添加任何代码,而将代码加入基类或全局。
三、基类调用派生的接口与多态的体现
就像上面说的,这样的奇异递归模板模式可以让基类调用派生类的接口。
下面我们看看具体的实现:
//基类
template<typename T>
class Human {
private:Human() {}friend T;public:T& toChild() {return static_cast<T&>(*this); //基类转派生类}void parenteat() {toChild().eat();}};//派生类1
class Men :public Human<Men> {
public:void eat() {std::cout << "男人喜欢吃面食!\n";}
};//派生类2
class Women :public Human<Women> {
public:void eat() {std::cout << "女人喜欢吃米饭!\n";}
};
在基类中,核心代码如下:
首先是转为派生类,然后调用派生类接口
Men mymen;
Women mywomen;//派生类给基类提供接口
mymen.parenteat();
mywomen.parenteat();
运行后我们得到:
这里实现了让基类调用我们派生类的接口,或者说是让派生类给基类提供接口。
我们也能在此基础上,添加一个函数模板:
template<typename T>
void myHumanFuncTest(Human<T>& tmpobj) {tmpobj.toChild().eat();
}
调用:
Men mymen;
Women mywomen;
myHumanFuncTest(mymen);
myHumanFuncTest(mywomen);
得到相同的结果:
实际上,可以发现,这是多态的体现,也是前面我们说过的静态多态。
这样的编程方法可以让我们更灵活:比如当 e a t ( ) eat() eat()函数是一个静态成员函数或者函数模板的时候,我们就无法使用虚函数进行动态多态,因此就必须使用奇异的递归模板模式了。