C++模板

栏目: 编程语言 · 发布时间: 7年前

内容简介:C++模板

假设我们有如下 比较 Fun

bool IsEqual (const int& left , const int& right)

{

return left == right;

此时我们比较 int 值是否相等->函数调用->    isEqual( 1, 2 );

但如果我们想比较 两个 string 对象是否相等, 则需要重新写一个Fun:

bool IsEqual (const string& left , const string& right)

{

return left == right;

string s1 = "yuanyuanyuan";

string s2 = "zhaobaba";

->isEqual( s1, s2 );

这样我们每比较一种类型,就需要重新写一个比较函数,做了大量的重复工作。

c++为了提高代码复用性,提出了模板函数概念:

下面是一个 比较 模板函数框架:

template<typename/*or class*/ T>
bool IsEqual( const T& left , const T& right )//传引用也是为了避免生成临时对象 or 临时变量  万一参数为string对象,则会拷贝构造生成临时对象,这里传引用是为了节省空间
{
	return left == right;//如果不是内置类型而是自己定义的类型, 则必须实现即重载 operator==( )函数
}

T代表我们传参数时传入的类型, 我们只需要在模板函数中将参数写为T, 具体函数调用时,编译器会通过实参推演形参类型

void test1( )
{
	string s1( "s1" ), s2( "s2" );

	cout << IsEqual( s1, s2 ) << endl;			//调用不同Fun  T分别为 string类型 与 int类型(编译器去做推演)  模板函数的实例化(编译器根据类型自动生成相应函数)
	cout << IsEqual( 1,1 ) << endl;				//通过实参推演形参类型
	//他们通过编译器实例化后是 重载函数 (模板实例化后生成代码(他们参数类型不同)所以重载)
}

模板函数   不同类型  T   是调用不同函数.

如果有模板函数和具体类型函数,优先使用有参数的函数(非模板),没有才使用模板函数

例:

bool IsEqual (const int& left , const int& right)//如IsEqual( 1, 2 )优先调用它,而不是下面的Fun( )
{
	return left == right;
} 

template <typename T>
bool IsEqual (const T& left , const T& right )
{
	return left == right;
}

模板参数匹配及显示实例化:

如第一种 只有一个模板参数T时  传  int 与 double 时,    不匹配   ->  isEqual( 1, 1.2 );

template <typename T>
bool IsEqual (const T& left , const T& right )
{
	return left == right;
} 

void test1 ()
{
	cout<<IsEqual (1,1)<<endl;
	cout<<IsEqual(1,1.2)<<endl; // 模板参数不匹配
	//下面是一种解决办法:
	//templat<class T1, class T2>   ( const T1& left , const T2& right )  
	//万一 left 传 int, right 传 double 第一种会出错, 此时必须这样定义( 第一种解决办法,另一种方法是显示实例化 )
	cout<<IsEqual<int>(给定类型  编译器不会推演,直接生成该类型代码)(1,1.2)<< endl; // 显示实例化(第二种办法)(T规定为int)
	cout<<IsEqual<double>(1,1.2)<< endl; // 显示实例化
}

没有对象实例化时,模板函数编译器不生成响应代码 , 证明:

template <typename T>
bool isequal (const t& left , const t& right )
{
	return left == right						//这没有“;”时  编译通过, 并没有错,因为没生成响应代码
}

不调用时:函数内部不进行语法检查

但 函数外部 会进行格式检查  如 bool IsEqual ( const T& x, const T& y   少个括号  编译时还是会报错的

模板函数,把属于我们的工作,从实参推演形参,交给编译器去做

说完了模板函数,我们来说说模板类。 同样的问题不只存在于模板函数中,模板类中也存在.

如 类中数据成员的 类型.

但模板类不能自己推演, 不能根据你传的参数推演T的类型,必须显示实例化!

需要注意的是模板类 类型为ClassName<T>, 类名为ClassName

但普通类, 类名和类类型相同

构造函数还有拷贝构造函数名字和类名相同

But拷贝构造Fun()参数(这个类类型必须加上类型T):

ClassName( const ClassName<T>& s );   经测试有无<T> 无影响

.h中声明

声明定义分离   标准!类中只有声明

.cpp中定义

类外面定义声明在类中的函数时:

类外定义时:

template<class T>//再写一次  模板

void SeqList<T>::Print( )//然后注意,这里的返回类型 为   SeqList<T>    类类型需要加<T>

{

...;

}

是T就加引用, 万一T是string , 深拷贝代价太大, 不改变就加 const ->const T&

拷贝构造参数不给引用会循环递归!  想想因为->拷贝构造未完成,所以会一直构造参数,递归死循环。  必须传引用

调试 时 栈溢出错误, 一般都是(递归)死循环

存在才赋值(赋值运算符重载),  不存在就拷贝构造.  s1 = s2  s1,s2均存在属于第一种情况。 string s2 = s1.  s2不存在属于第二种情况。

有了类模板参数后,我们就可以实现容器适配器:

队列:队尾进,对头出,插入在队尾

栈:栈顶进,   栈底->栈顶

适配器模式实现一个队列:

使用  List 实现容器适配器:

template<class T, class Container>
class Queue
{
public:
	void Push( const T& x );
	{
		_con.PushBack( x );
	}

	void Pop( )
	{
		_con.PopFront( );
	}

	T& Front( )									//根据要实现的函数功能(此处为队列),再决定在里面调用容器(此处为List)的什么函数
	{
		return _con.Front( );
	}

	T& Back( )									//因为出了作用域还存在!  所以T&
	{
		return _con.Back( );
	}

	size_t Size( )
	{
		return _con.Size( );
	}

	bool IsEmpty( )
	{
		return _con.IsEmpty( );
	}
protected:
	Container _con;
};

void TestQueue( )
{
	Queue<int, List<int>> q;			//我们实现一个 List 容器(类模板List), 并且List中 有声明我们使用的Fun()
q.Push( 1 );

	while (!q.IsEmpty( ))
	{
		cout << q.Front( ) << " ";

		q.Pop( );
	}
}
template<class T, class Container = List<T>>	//Stack<int> s 这样也可以   模板的缺省参数
class Stack
{
public:
	void Push( const T& x )
	{
		_con.PushBack( x );
	}

	T& Top( )									//因为栈后进先出,所以Top是最后进来的, 所以是返回Back( ).
	{
		return _con.Back( );
	}

protected:
	Container _con;
};

void TestStack( )								//若是链表,删除时    释放空间,插入又开空间。 不停释放开辟不好,但顺序表不需要,   原空间反复利用,  它删除只是 --size
{
	Stack<int, SeqList<int>> s;

	s.Push( 1 );

	while ( !s.IsEmpty( ) )
	{
		cout << s.Top( ) << " ";
		s.Pop( );
	}
}

模板的模板参数:接上面,若这样传参数,Stack<int, SeqList<char>> s;  也就是这个 char 传错 会存在隐患。

为了避免错误,我们进行改进:template<class T, template<class(不用传参数,和T类型一样)>class Container = SeqList(缺省的模板的模板参数)>  (传一个类名,而不是传一个类型)。  避免传char等  出错。

例:Stack<int, List> s;

const size_t N = 100;

template<class T>

class SeqList

{

protected:

T _a[N];

size_t size;

};

非类型模板参数:

现在生成对象,   _a大小只能是200。  不管定义几个。  为了解决这个问题 -> 非类型模板参数。 N是一个常量,可以给缺省参数  它也可以做模板函数的非类型模板参数。

template<class T(类型模板参数),size_t N = 200>

SeqList<int, 200> s1;

SeqList<double, 2000> s2;

浮点数和类对象不允许做非类型模板参数

模板的特化:

1.全特化::

template <typename T>
class SeqList
{ 
public :
	SeqList();
	~ SeqList();
private :
	int _size ;
	int _capacity ;
	T* _data ;
};

template<typename T>
SeqList <T>:: SeqList()
	: _size(0)
	, _capacity(10)
	, _data(new T[ _capacity])
{
	cout<<"SeqList<T>" <<endl;
}

template<typename T>
SeqList <T>::~ SeqList()
{
	delete[] _data ;
}
template <>
class SeqList <int>
{ 
public :
	SeqList(int capacity);
	~ SeqList();
private :
	int _size ;
	int _capacity ;
	int* _data ;
};

特化后定义成员函数不再需要模板形参

SeqList <int>:: SeqList(int capacity)
	: _size(0)
	, _capacity(capacity )
	, _data(new int[ _capacity])
{
	cout<<"SeqList<int>" <<endl;
} 

// 特化后定义成员函数不再需要模板形参
SeqList <int>::~ SeqList()
{
	delete[] _data ;
} 

void test1 ()
{
	SeqList<double > sl2;
	SeqList<int > sl1(2);//优先调用特化部分(调用才生成代码,没有特化的,才是T)
}
template <typename T1, typename T2>
class Data
{ 
public :
	Data();
private :
	T1 _d1 ;
	T2 _d2 ;
};

template <typename T1, typename T2>
Data<T1 , T2>:: Data()
{
	cout<<"Data<T1, T2>" <<endl;
}

局部特化第二个参数:

template <typename T1>
class Data <T1, int>
{
public :
	Data();	
private :
	T1 _d1 ;
	int _d2 ;
};

template <typename T1>
Data<T1 , int>:: Data()
{
	cout<<"Data<T1, int>" <<endl;
}

ps:下面的例子可以看出,偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本。

局部特化两个参数为指针类型

template <typename T1, typename T2>
class Data <T1*, T2*>
{ 
public :
	Data();
private :
	T1 _d1 ;
	T2 _d2 ;
	T1* _d3 ;
	T2* _d4 ;
};

template <typename T1, typename T2>
Data<T1 *, T2*>:: Data()
{
	cout<<"Data<T1*, T2*>" <<endl;
}

局部特化两个参数为引用:

template <typename T1, typename T2>
class Data <T1&, T2&>
{ 
public :
	Data(const T1& d1, const T2& d2);
private :
	const T1& _d1;
	const T2& _d2;
	T1* _d3 ;
	T2* _d4 ;
};

template <typename T1, typename T2>
Data<T1&, T2&>:: Data(const T1& d1, const T2& d2)
	: _d1(d1 )
	, _d2(d2 )
{
	cout<<"Data<T1&, T2&>" <<endl;
} 

void test2 ()
{
	Data<double , int> d1;
	Data<int , double> d2;
	Data<int*, int*> d3;
	Data<int&, int&> d4(1, 2);
}

模板的全特化和偏特化都是在已定义的模板基础之上,不能单独存在。

template <typename T1>			//如果都是具体类型  则 template<>里面啥都不用写,因为不是模板参数,都是具体类型
Date<T1, int>:: Date( )			//第二个参数特化为 int 类型
{
	;
}
template <typename T1, typename T2>
class Data<T1*, T2*>
调用:	Data<int*, double*>

template <typename T1, typename T2>
class Data<T1, T2*>
调用:	Data<int, double*>
1.全特化:template<class T>
	  class Name

2.偏特化:template<>
	  class Name<int>
	  template<class T>
	  class Name<T*>

1.进一步条件限制模板参数

2.多个模板参数时,特化部分参数

编译错误:语法

链接错误:找不到具体定义它地方

模板的分离编译:声明,定义分离

ClassName.h声明

ClassName.cpp实现(这样则链接错误)

Test.cpp测试

template<class T>

void Data<T>::F( )

{}

非模板类是可以分离编译的

模板不支持分离编译:

分离编译后,找不到定义!找不到cpp中的定义,因为大家分开编译(main cpp) 所以, cpp中模板未实例化,未生产相应代码。所以链接错误!

模板实例化才生成具体代码,才语法检查(但并不是不实例化时什么语法都不检查!)

main.cpp

Data.h

Data.cpp

预处理(预编译)(展开头文件,替换宏,去注释,条件编译,处理行号文件名函数名(__FILE__(也是一个宏)等))->main.i 和 Data.i

编译(检查语法错误,)->main.s  汇编代码   一句代码对应多句汇编代码 ++i  (mov  eax, i) -> (add eax) -> (mov  i, eax)  cpu不能识别汇编代码  main.s  Data.s

汇编   汇编代码->二进制机器码(这是一个过程)main.o  Data.o 符号表(函数名+地址  F:0x1122)

链接->.exe(Windows)     a.out(Linux)   可执行文件

函数变成指令,第一句指令即Fun 地址

分离编译->链接错误(没有找到定义->函数的地址)

非模板类 .cpp .h

模板类 .hpp(声明定义放一起)

.hpp:

//分离编译

template<class T>
class Data
{
public:
	void F( );
protected:
	T _d;
};

template<class T>
void Data<T>/*类型*/::F( )
{
	;
}

以上所述就是小编给大家介绍的《C++模板》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Tagging

Tagging

Gene Smith / New Riders / 2007-12-27 / GBP 28.99

Tagging is fast becoming one of the primary ways people organize and manage digital information. Tagging complements traditional organizational tools like folders and search on users desktops as well ......一起来看看 《Tagging》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具