c/c++开发,无可避免的模板编程实践(篇三)-模板与多态、指针及元编程

目录

一、模板与多态

        1.1 函数模板本身就是静多态

        1.2 模板的动多态

二、模板元编程

三、模板与指针


一、模板与多态

        多态就是通过单一命名标记关联不同特定行为的能力。在C++中,主要是通过继承和虚函数来实现,由于继承和虚函数主要是在运行期进行处理,因此c++把这种多态称为“动多态”。而通过函数重载方式也可以单一命名标记关联不同行为,也是多态,这种方式是在编译期进行处理的,因此称为“静多态”。

        在前文就阐述过,函数模板就是一个家族函数,这就意味着它是具有单一命名标记关联不同特定行为的能力。其实从泛的层面上来说,无论是函数模板或是类模板,其本身都具备多态能力,并还可以向普通函数或类那样支持到重载、继承、虚函数,其多态特性就具有更多层次性和编码样式。

        1.1 函数模板本身就是静多态

        普通函数通过重载实现多态,定义了一组同函数名但参数类型及数量不同的函数,在实际函数调用时,编译器由传入的实参去推演采用哪个函数,从而实现不同特定行为的能力

void doit(const int &val){std::cout<<"doit int\n";};
void doit(const char &val){std::cout<<"doit char\n";};
void doit(const double &val){std::cout<<"doit double\n";};
void doit(const char &cval, const int &ival){std::cout<<"doit char and int\n";};
//
doit(10);
doit(2.5);
doit('a',100);

        而函数模板同样能达到此效果,支持特定行为更多,并更简洁:

template <typename T>
void doit_template(const T &val){std::cout<<"doit_template const T\n";};
template <typename T>
void doit_template(const T *val){std::cout<<"doit_template const T*\n";};		//函数模板重载
template <typename T1, typename T2>
void doit_template(const T1 &val1,const T2 &val2){std::cout<<"doit_template const T1,T2\n";};//函数模板重载
//
doit_template(10);
doit_template(2.5);
doit_template('a',100);
//
typedef void (*pdoit)(const int &val);
pdoit pf = &doit;
doit_template<pdoit>(pf);

        在看看下面这个例子,通过"..."省略号参数支持,实现不定参数集:

//
template <typename T>
void doit_arg(const T &val,...){std::cout<<"doit_arg const T ...\n";};
//
doit_arg(1,2,3);

        然后也将该函数进行重载:

//
template <typename T>
void doit_arg(const T &val,...){std::cout<<"doit_arg const T ...\n";};
template <typename T>
void doit_arg(const T &val){std::cout<<"doit_arg const T \n";};
//
doit_arg(1,2,3);    //OK
doit_arg(1);        //error,两个函数模板都满足,编译器无法推演

        但采用普通函数去重载,调用时是能识别的,因为普通函数比模板函数更具有确定性,编译器会优先精确匹配到它,注意该函数并不是模板函数的特例化(它没带模板标记)。另外如果是在模板之间选择匹配的话,特化程度更高的模板优先被选择。

//
template <typename T>
void doit_arg(const T &val,...){std::cout<<"doit_arg const T ...\n";};
template <typename T>
void doit_arg(const T &val){std::cout<<"doit_arg const T \n";};
void doit_arg(const double &val){std::cout<<"doit_arg const double \n";};
//
doit_arg(1,2,3);    //OK
//doit_arg(1);      //error
doit_arg(1.2);      //OK

        不同于普通类通过继承及虚函数来实现多态,类模板本身能实现多态,从实现方式来看,它可以归并在静多态方面。

template <typename T>
class ATest	//类模板本身能实现多态
{
public:
	T val;    //可以传入自定义的类型,将多态性封装在T来实现
};

//
ATest<int> i_c;
ATest<float> f_c;
ATest<std::string> s_c;

        1.2 模板的动多态

        类模板和普通模板一样,是可以被继承的,但有和普通模板不一样,类模板的继承有更多的设计选择。

template <typename T>
class ATest	//类模板本身能实现多态
{
public:
	T val;	//可以传入自定义的类型,将多态性封装在T来实现
};

template <typename T>
class AChild1 : public ATest<T>
{
public:
	T c_val;	
};

template < typename T=int>
class AChild2 : public ATest<int>
{
public:
	int c_val;	
};

class AChild3 : public ATest<char>
{
public:
	char c_val;	
};

template < typename T1,typename T2>
class AChild4 : public ATest<T1>
{
public:
	T2 c_val;	
};

template <typename T>
class AChild5 : virtual public ATest<T>
{
public:
	T c_val;	
};

template <typename T>
class AChild6 : private ATest<T>
{
public:
	T c_val;	
};

//
	AChild1<double> ichild1;
	ichild1.val = 10.2;
	ichild1.c_val = 11.5;
	//
	AChild2<> ichild2;
	ichild2.val = 10;
	ichild2.c_val = 15;
	//
	AChild3 ichild3;
	ichild3.val = 'a';
	ichild3.c_val = 'b';
	//
	AChild4<int,float> ichild4;
	ichild4.val = 10;
	ichild4.c_val = 20.3;
    //
	AChild5<int> ichild5;
	ichild5.val = 1;
	ichild5.c_val = 5;
	//
	AChild6<float> ichild6;
	//ichild6.val = 2.6; //私继承
	ichild6.c_val = 5.9;

        仅仅就是类继承这一特点,类模板就能演化成不同的继承设计,支持特例化继承、virtual继承、私继承、缺省默认模板参数继承、模板参数扩展继承等等。

        模板参数也是类型,那么在继承实现中,可以继承模板参数

template <typename T>
class AChild7 : public T
{
public:
	AChild7():T()
	{
		
	}
	~AChild7()
	{
		
	}
};

template <typename T>
class AChild8 : public T,public ATest<T>    //多重继承
{
	public:
	AChild8():T()
	{
		
	}
	~AChild8()
	{
		
	}
};
//
template <typename T,typename Base>
class AChild10 : public Base,public ATest<T>    //多重继承
{
	public:
	AChild10():Base(),ATest<T>()
	{
		
	}
	~AChild10()
	{
		
	}
};
//
	AChild7<std::string> ichild7;
	ichild7.append("hello");
	std::cout<<"ichild7 size = "<<ichild7.size() <<std::endl;
	//
	AChild8<std::string> ichild8;
	ichild8.val = "hi";

        但将模板参数作为基类来继承,也会引入风险,就是前文讲述的,使用者需要自行把控传入模板参数类型的行为,例如上述继承中,传入一个int型(原生类型等)

AChild7<int> ichild7_;	//error: base type 'int' fails to be a struct or class type

        另外,为基类模板参数设置默认缺省值,派生类在不指定类型时,就能实现继承。

template <typename T=int>
//template <typename T>
class ATest	//类模板本身能实现多态
{
public:
	T val;	//可以传入自定义的类型,将多态性封装在T来实现
};
//下面三种继承是一致的
template < typename T=int>
class AChild2 : public ATest<int>
{
public:
	int c_val;	
};

class AChild3 : public ATest<char>
{
public:
	char c_val;	
};
class AChild9 : private ATest<>
{
	public:	
};

        既然基类能作为模板参数,那么派生类用来作为模板参数呢,这种倒施逆行的方式在一些特定场景可能会有奇效哦。

template <typename Derived>
class MyBase
{
	public:
	void doSomething(Derived val)
	{
		val.doSomething();
	};
};

class MyDerived : public MyBase<MyDerived>
{
	public:
	void doSomething(void)
	{
		std::cout<< "just boring!" <<std::endl;
	};
};

//
	MyDerived myd;
	myd.doSomething();

        类模板和普通类一样,支持到普通函数成员虚拟化设计,但函数模板成员不能声明为虚函数,因为虚函数调用机制实现使用了一个大小固定的表,每个虚函数都对应表的一个入口,但成员函数模板在构建虚函数表之前是无法确定的。

//模板-虚函数
template <typename T>
class VClass
{
	public:
	virtual void doit()
	{
		std::cout<< "VClass doit"<<std::endl;
	};
};

template <typename T>
class MYVBase : public VClass<T>
{
	public:
	virtual void doit()
	{
		std::cout<< "MYVBase doit"<<std::endl;
	};
};
template <typename T>
class MYVBase_C : public VClass<T>
{
	public:
};

//
	MYVBase<int> vbc;
	vbc.doit();        //MYVBase doit
	MYVBase_C<char> vbc_c;
	vbc_c.doit();      //VClass doit

        或者将基类作为派生类的模板参数,同样能实现虚函数效果

//模板-虚函数-另类
class VirtualClass
{
	public:
	virtual void doit()
	{
		std::cout<< "VirtualClass doit"<<std::endl;
	};
};

class NVirtualClass
{
	public:
};

template <typename VBase>
class MYNewVBase : public VBase
{
	public:
	void doit()
	{
		std::cout<< "MYNewVBase doit"<<std::endl;
	};
};

template <typename VBase>
class MYNewVBase_C : public VBase
{
	public:
};
//
	MYNewVBase<VirtualClass> nvbc;
	nvbc.doit();
	MYNewVBase_C<VirtualClass> nvbc_c;
	nvbc_c.doit();
	MYNewVBase<NVirtualClass> nvbc_;
	nvbc_.doit();

二、模板元编程

        模板实例化机制是一种递归语言机制,可以用于在编译期执行复杂计算,这种模板实例化所展现的编译器计算被叫做template meta programming。看下面的例子:

//
template <int N>
class POW2
{
	public:
	enum{ret=2*POW2<N-1>::ret};
};

template <>
class POW2<0>
{
	public:
	enum{ret=1};
};

//
std::cout<<"POW2<5>::ret = "<< POW2<5>::ret <<std::endl;   //ret = 32

        上面展示的是通过递归机制在编译期计算2的N次幂。编译器在实例化时,会不断递归下去,直到特例化POW2<0>结束。注意,枚举值POW2<5>::ret=32是编译期就确定下来的。替换静态常量也能达到同样效果。

        模板元编程可以通过展开循环来优化迭代效率,例如我们要计算两个数组的点乘,通常做法是:

for(int i=0; i<N; i++)
{
    result += a[i]*b[i];
}

        对于N很大的情况,其实这种循环计算效率是很慢的,但是如果在编译期展开计算表达式,在运行期直接计算,那么运行效率是很高的,这方面类似我们在嵌入式领域时对于字符编码、图像矩阵等就常常采用展开的方式,当然那些很多展开都是借助辅助工具自动创建的:

retsult = a[0]*b[0]+a[1]*b[1]+...+a[N-1]*b[N-1];

        对于我们自行编码时,如果我们手动重复撰写这些计算公式展开肯定很乏味,采用模板元编程可以解决这些烦恼

//基本模板
template <int DN, typename T>
class dotMultip{
	public:
	static T retsult (T* a, T* b)
	{
		return (*a)*(*b)+dotMultip<DN-1,T>::retsult(a+1,b+1);
	};
};
//特例化,循环结束
template < typename T>
class dotMultip<1,T>{
	public:
	static T retsult (T* a, T* b)
	{
		return (*a)*(*b);
	}
};
//辅助函数,初次调用进入循环
template <int DN, typename T>
inline T dotMultipFunc(T* a, T* b){
	return dotMultip<DN,T>::retsult(a,b);
};

//
	int a[5]={1,2,3,4,5};
	int b[5]={2,3,4,5,6};
	std::cout<<"dotMultipFunc<5>(a,b) = "<< dotMultipFunc<5>(a,b) <<std::endl;//=70

        在实例化过程中,dotMultipFunc<5>(a,b)的调用时如下过程展开:

dotMultipFunc<5>(a,b)
=dotMultip<5,int>::retsult(a,b)
=(*a)*(*b)+dotMultip<4,int>::retsult(a+1,b+1)
=(*a)*(*b)+(*(a+1))*(*(b+1))+dotMultip<3,int>::retsult(a+2,b+2)
=(*a)*(*b)+(*(a+1))*(*(b+1))+(*(a+2))*(*(b+2))+dotMultip<2,int>::retsult(a+3,b+3)
=(*a)*(*b)+(*(a+1))*(*(b+1))+(*(a+2))*(*(b+2))+(*(a+3))*(*(b+3))+dotMultip<1,int>::retsult(a+4,b+4)
=(*a)*(*b)+(*(a+1))*(*(b+1))+(*(a+2))*(*(b+2))+(*(a+3))*(*(b+3))+(*(a+4))*(*(b+4))

       当然,这运行效率的提高是有代价的,首先,数组的元数在编译期是已知的,并由于编译期展开的原因,可能会带来编译效率的下降,至于是否需要选用,取决实际项目中要求的权衡了。

三、模板与指针

        智能指针,本质上通过类模板可以有效的封装内存操作,管理使用基础对象的操作,能够动态分配和释放相关继承类的对象,这就是我们所描述的智能指针。

//智能指针示例
template < typename T>
class Handle
{
	public:
	Handle()
		:ptr(0)
	{
	};
	explicit Handle(T*p)
		:ptr(p)
	{
	};
	~Handle(){
		delete ptr;
		ptr = 0;
		std::cout<<"~Handle()\n";
	};
	//拷贝构造
	Handle(Handle<T> const&rhs)
	{
		//delete ptr;
		ptr = rhs.ptr;
	};
	//拷贝赋值运算
	Handle<T>& operator=(const Handle &rhs)
	{
		delete ptr;
		ptr = rhs.ptr;
		return *this;
	};
	//指针运算符
	T& operator*() const
	{
		assert(0!=ptr);
		return *ptr;
	};
	//指针运算符
	T* operator->() const
	{
		assert(0!=ptr);
		return ptr;
	};
	private:
	T *ptr;
	//
};
//
//
	Handle<MYNewVBase_C<VirtualClass> > first_hclass(new MYNewVBase_C<VirtualClass>());
	first_hclass->doit();
	(*first_hclass).doit();
	Handle<MYNewVBase_C<VirtualClass> > second_hclass = first_hclass;
	second_hclass->doit();

        上述例子,通过模板参数typename T实现对实体类指向,并给Handle模板赋予指针运算符“*”和"&",使得使用实体类T时,和以其本身实例化指针时使用是一致的。

四、源码

        建立test.h/cpp源文件,g++ test.cpp -o test.exe编译输出,测试如下:

         test.h

#ifndef _TEST_H_
#define _TEST_H_
#include <iostream>
#include <string>
#include <stdio.h>
#include <cassert>

//1.1 函数模板本身就是静多态
void doit(const int &val){std::cout<<"doit int\n";};
void doit(const char &val){std::cout<<"doit char\n";};
void doit(const double &val){std::cout<<"doit double\n";};
void doit(const char &cval, const int &ival){std::cout<<"doit char and int\n";};

template <typename T>
void doit_template(const T &val){std::cout<<"doit_template const T\n";};
template <typename T>
void doit_template(const T *val){std::cout<<"doit_template const T*\n";};		//函数模板重载
template <typename T1, typename T2>
void doit_template(const T1 &val1,const T2 &val2){std::cout<<"doit_template const T1,T2\n";};//函数模板重载

//
template <typename T>
void doit_arg(const T &val,...){std::cout<<"doit_arg const T ...\n";};
template <typename T>
void doit_arg(const T &val){std::cout<<"doit_arg const T \n";};
void doit_arg(const double &val){std::cout<<"doit_arg const double \n";};

//1.2 模板的动多态
template <typename T=int>
//template <typename T>
class ATest	//类模板本身能实现多态
{
public:
	T val;	//可以传入自定义的类型,将多态性封装在T来实现
};

template <typename T>
class AChild1 : public ATest<T>
{
public:
	T c_val;	
};

template < typename T=int>
class AChild2 : public ATest<int>
{
public:
	int c_val;	
};

class AChild3 : public ATest<char>
{
public:
	char c_val;	
};

template < typename T1,typename T2>
class AChild4 : public ATest<T1>
{
public:
	T2 c_val;	
};

template <typename T>
class AChild5 : virtual public ATest<T>
{
public:
	T c_val;	
};

template <typename T>
class AChild6 : private ATest<T>
{
public:
	T c_val;	
};

template <typename T>
class AChild7 : public T
{
public:
	AChild7():T()
	{
		
	}
	~AChild7()
	{
		
	}
};

template <typename T>
class AChild8 : public T,public ATest<T>
{
	public:
	AChild8():T()
	{
		
	}
	~AChild8()
	{
		
	}
};

class AChild9 : private ATest<>
{
	public:	
};

//
template <typename T,typename Base>
class AChild10 : public Base,public ATest<T>    //多重继承
{
	public:
	AChild10():Base(),ATest<T>()
	{
		
	}
	~AChild10()
	{
		
	}
};

template <typename Derived>
class MyBase
{
	public:
	void doSomething(Derived val)
	{
		val.doSomething();
	};
};

class MyDerived : public MyBase<MyDerived>
{
	public:
	void doSomething(void)
	{
		std::cout<< "just boring!" <<std::endl;
	};
};

template <typename Derived>
class MyCBase
{
	public:
	void doSomething(Derived val)
	{
		val.doSomething();
	};
};

template <typename T>
class MyCDerived : public MyCBase<MyCDerived<T> >
{
	public:
	void doSomething(const T &val)
	{
		std::cout<< "just boring =" << val <<std::endl;
	};
};
//模板-虚函数
template <typename T>
class VClass
{
	public:
	virtual void doit()
	{
		std::cout<< "VClass doit"<<std::endl;
	};
};

template <typename T>
class MYVBase : public VClass<T>
{
	public:
	virtual void doit()
	{
		std::cout<< "MYVBase doit"<<std::endl;
	};
};
template <typename T>
class MYVBase_C : public VClass<T>
{
	public:
};
//模板-虚函数-另类
class VirtualClass
{
	public:
	virtual void doit()
	{
		std::cout<< "VirtualClass doit"<<std::endl;
	};
};

class NVirtualClass
{
	public:
};

template <typename VBase>
class MYNewVBase : public VBase
{
	public:
	void doit()
	{
		std::cout<< "MYNewVBase doit"<<std::endl;
	};
};

template <typename VBase>
class MYNewVBase_C : public VBase
{
	public:
};
//模板元编程
template <int N>
class POW2
{
	public:
	enum{ret=2*POW2<N-1>::ret};
	static int const retsult=2*POW2<N-1>::retsult;
};

template <>
class POW2<0>
{
	public:
	enum{ret=1};
	static int const retsult=1;
};
//基本模板
template <int DN, typename T>
class dotMultip{
	public:
	static T retsult (T* a, T* b)
	{
		return (*a)*(*b)+dotMultip<DN-1,T>::retsult(a+1,b+1);
	};
};
//特例化,循环结束
template < typename T>
class dotMultip<1,T>{
	public:
	static T retsult (T* a, T* b)
	{
		return (*a)*(*b);
	}
};
//辅助函数,初次调用进入循环
template <int DN, typename T>
inline T dotMultipFunc(T* a, T* b){
	return dotMultip<DN,T>::retsult(a,b);
};
//智能指针示例
template < typename T>
class Handle
{
	public:
	Handle()
		:ptr(0)
	{
	};
	explicit Handle(T*p)
		:ptr(p)
	{
	};
	~Handle(){
		delete ptr;
		ptr = 0;
		std::cout<<"~Handle()\n";
	};
	//拷贝构造
	Handle(Handle<T> const&rhs)
	{
		//delete ptr;
		ptr = rhs.ptr;
	};
	//拷贝赋值运算
	Handle<T>& operator=(const Handle &rhs)
	{
		delete ptr;
		ptr = rhs.ptr;
		return *this;
	};
	//指针运算符
	T& operator*() const
	{
		assert(0!=ptr);
		return *ptr;
	};
	//指针运算符
	T* operator->() const
	{
		assert(0!=ptr);
		return ptr;
	};
	private:
	T *ptr;
	//
};

#endif //_TEST_H_

        test.cpp

#include "test.h"

int main(int argc, char* argv[])
{
	doit(10);
	doit(2.5);
	doit('a',100);
	//
	doit_template(10);
	doit_template(2.5);
	doit_template('a',100);
	typedef void (*pdoit)(const int &val);
	pdoit pf = &doit;
	doit_template<pdoit>(pf);
	//
	doit_arg(1,2,3);
	//doit_arg(1);
	doit_arg(1.2);
	//
	ATest<int> i_c;
	ATest<float> f_c;
	ATest<std::string> s_c;
	//
	AChild1<double> ichild1;
	ichild1.val = 10.2;
	ichild1.c_val = 11.5;
	//
	AChild2<> ichild2;
	ichild2.val = 10;
	ichild2.c_val = 15;
	//
	AChild3 ichild3;
	ichild3.val = 'a';
	ichild3.c_val = 'b';
	//
	AChild4<int,float> ichild4;
	ichild4.val = 10;
	ichild4.c_val = 20.3;
	//
	AChild5<int> ichild5;
	ichild5.val = 1;
	ichild5.c_val = 5;
	//
	AChild6<float> ichild6;
	//ichild6.val = 2.6; //私继承
	ichild6.c_val = 5.9;
	//
	AChild7<std::string> ichild7;
	ichild7.append("hello");
	std::cout<<"ichild7 size = "<<ichild7.size() <<std::endl;
	//
	AChild8<std::string> ichild8;
	ichild8.val = "hi";
	//
	//AChild7<int> ichild7_;	//error: base type 'int' fails to be a struct or class type
	//
	MyDerived myd;
	myd.doSomething();
	//
	MyCDerived<int> mycd;
	mycd.doSomething(100);
	//
	MYVBase<int> vbc;
	vbc.doit();
	MYVBase_C<char> vbc_c;
	vbc_c.doit();
	//
	MYNewVBase<VirtualClass> nvbc;
	nvbc.doit();
	MYNewVBase_C<VirtualClass> nvbc_c;
	nvbc_c.doit();
	MYNewVBase<NVirtualClass> nvbc_;
	nvbc_.doit();
	//
	std::cout<<"POW2<5>::ret = "<< POW2<5>::ret <<std::endl;
	std::cout<<"POW2<5>::retsult = "<< POW2<5>::retsult <<std::endl;
	//
	int a[5]={1,2,3,4,5};
	int b[5]={2,3,4,5,6};
	std::cout<<"dotMultipFunc<5>(a,b) = "<< dotMultipFunc<5>(a,b) <<std::endl;
	//
	Handle<MYNewVBase_C<VirtualClass> > first_hclass(new MYNewVBase_C<VirtualClass>());
	first_hclass->doit();
	(*first_hclass).doit();
	Handle<MYNewVBase_C<VirtualClass> > second_hclass = first_hclass;
	second_hclass->doit();
	return 0;
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

py_free-物联智能

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值