建构网站西安,最近一周新闻大事摘抄,摄影网站免费,贷款网站平台有哪些一、奇异的递归模板模式范例
奇异的递归模板模式 ( 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)不是一种新技术#xff0c;而是一种模板编程中使用的编程手…一、奇异的递归模板模式范例
奇异的递归模板模式 ( 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)不是一种新技术而是一种模板编程中使用的编程手法把派生类作为基类的模板参数。
下面是一个简单范例
//基类
templatetypename T
class Base {//...
};//派生类1
class Derived1 : public BaseDerived1 { //派生类继承基类的派生类参数的泛型
public:void myfunc() {std::cout Derived1::myfunc()执行了\n;}};//派生类2(模板)
templatetypename T
class Derived2 : public BaseDerived2T {};这样的继承方式在模板与泛型编程中很常见。 以下介绍一下基本的几个用法。
二、在基类中使用派生类对象
一般情况下我们可以通过 s t d : : s t a t i c _ c a s t T std::static\_castT std::static_castT静态类型转换将基类对象转换为派生类对象从而在基类对象内使用派生类对象的接口。 如下代码所示
templatetypename T
class Base {private:Base() {}friend T; //只有在类内实例化BaseT的T是友元public:void asDerived() {T derived static_castT(*this); //将基类转为派生类derived.myfunc();}
};//派生类1
class Derived1 : public BaseDerived1 { //派生类继承基类的派生类参数的泛型
public:void myfunc() {std::cout Derived1::myfunc()执行了\n;}};
为了防止继承时出错我们在基类内增加一个友元类 T T T只对相同派生类的类模板开放构造函数。
使用友元可以避免下面的错误
相应的友元模板知识点可以参考友元类模板这里就不赘述了。 注意这里的写法 当然这个派生类也可以是一个模板
//派生类2(模板)
templatetypename T
class Derived2 : public BaseDerived2T {
public:void myfunc() {std::cout Derived2T::myfunc()执行了\n;}
};
同时我们在基类增加友元声明
templatetypename U
friend class Derived2; // 允许所有 Derived2 的实例成为友元这样我们实例化出的派生类都支持基类的私有访问了调用下方测试代码
void Test1() {Derived1 myd1;myd1.asDerived();Derived2int myd2;myd2.asDerived();
}这样的运行结果是我们在基类调用了派生类的方法也就是可以看作是派生类为基类提供了接口这与一般的继承调用不同基类给派生类提供接口 运行结果如下
三、基于减少派生类代码量的考虑
通常如果我们需要在派生类内增加功能我们需要不断的补充代码而这里的做法是我们把一部分代码放入基类实现让派生类内的代码相对简洁一些。
下面是一个例子
//基于减少派生类中代码量的考虑templatetypename T
struct shape {//友元实现运算符重载friend bool operator(const shapeT obj1, const shapeT obj2) {const T objtmp1 static_castconst T(obj1);const T objtmp2 static_castconst T(obj2);if (!(objtmp1 objtmp2) !(objtmp2 objtmp1)) return true;return false;}
};struct square :public shapesquare {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\_castT std::static_castT对传入的基类对象转为相应的派生类对象最后进行比较即可。
调用测试代码
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;}
这样我们在派生类中无需添加任何代码而将代码加入基类或全局。 三、基类调用派生的接口与多态的体现
就像上面说的这样的奇异递归模板模式可以让基类调用派生类的接口。 下面我们看看具体的实现
//基类
templatetypename T
class Human {
private:Human() {}friend T;public:T toChild() {return static_castT(*this); //基类转派生类}void parenteat() {toChild().eat();}};//派生类1
class Men :public HumanMen {
public:void eat() {std::cout 男人喜欢吃面食\n;}
};//派生类2
class Women :public HumanWomen {
public:void eat() {std::cout 女人喜欢吃米饭\n;}
};
在基类中核心代码如下 首先是转为派生类然后调用派生类接口
Men mymen;
Women mywomen;//派生类给基类提供接口
mymen.parenteat();
mywomen.parenteat();运行后我们得到 这里实现了让基类调用我们派生类的接口或者说是让派生类给基类提供接口。
我们也能在此基础上添加一个函数模板
templatetypename T
void myHumanFuncTest(HumanT tmpobj) {tmpobj.toChild().eat();
}调用
Men mymen;
Women mywomen;
myHumanFuncTest(mymen);
myHumanFuncTest(mywomen);得到相同的结果 实际上可以发现这是多态的体现也是前面我们说过的静态多态。
这样的编程方法可以让我们更灵活比如当 e a t ( ) eat() eat()函数是一个静态成员函数或者函数模板的时候我们就无法使用虚函数进行动态多态因此就必须使用奇异的递归模板模式了。