C++第20天 模板与泛型

/*


类模板:
	以关键字template开头,后接模板形参表。
	在函数模板形参表中,关键字typename和class具有相同含义,可以呼唤使用,而且两个关键字都可以在同一模板形参表值哦功能使用,例如:
		template<typename T,class V> calc(const T&,const U&);

	在模板定义内部指定类型:
		如果要在函数模板内部使用类定义类型成员,则必须告诉编译器我们正在使用的名字指的是一个类型,必须显式这样做,因为编译器不知到由类型形参定义的名字何时是一个类型,何时是一个值。例如:
			template <class Parm,class U> Parm fcn(Parm* array,U value){
				//不能判定size_type是一个类型还是一个数据成员
				//编译器默认它是一个数据成员
				Parm::size_type *P;
			}
		我们直到size_type肯定是绑定到Parm的那个类型成员,但我们不知道size_type是一个类型成员还是一个数据成员的名字,默认情况下,编译器假定这样的名字指定数据成员,而不是类型。
		如果希望编译器将size_type当作类型,则必须显式告诉编译器这样做:
			typelate<class Parm,class U> Parm fcn(Parm* array,U value){
				//显式告诉编译器size_type是一个类型
				typename Parm::size_type* p;
			}
		通过在成员名前加上关键字typename作为前缀,可以告诉编译器将成员当成类型。
	(注:如果不确定是否需要以typename指明一个名字是一个类型,那么指定它是个好主意,在类型之前指定typename没有害处,即使typename是不必要的,也没有关系。)

	
非类型模板形参:在调用函数时非类型形参将用值代替,值的类型在模板形参表中指定,例如:
	template<class T,size_t N> void array_init(T (&parm)[N]){
		for(size_t i=0;i!=N;++i){
			parm[i]=0;
		}
	}
	模板非类型形参是模板定义内部的常量值,在需要常量表达式的使用,可使用非类型形参指定数组的长度。

编写独立于类型的代码的两个重要原则:
	1.模板的形参是const引用。
	2.函数体中的测试只用<比较。

类型形参的是惨的受限转换:
	1.const转换:接受const引用或const指针的函数可以分别用非const对象的引用或指针来调用,无需产生新的实例化。如果函数接受非引用类型,形参类型和实参都忽略const,即无乱传递const或非const对象给接受非引用类型的函数,都使用相同的实例化。
	2.数组或函数到指针的专换:如果模板形参不是引用类型,则对数组或函数类型的的实参应用常规指针转换。数组实参将当作指向其第一个元素的指针,函数实参当作指向函数类型的指针。
	
模板编译模型:
	1.包含编译模型:
		编译器必须看到所有模板的定义。一般而言,可以通过在声明函数模板或类模板的头文件中添加一条#include指示使定义可用,该#include引入了包含相关定义的源文件,例如:
			//utilities.h头文件的内容
			#ifndef UTLITIES_H
			#define UTLITIES_H
			template<class T> int compare(const T&,const T&);
			//其他声明
			#include "utilities.cpp"
			#endif
			//utilities.cpp源文件的内容
			template<class T> int compare(const T &v1,const T &v2){
				if(v1<v2) reutrn -1;
				if(v2<v1) return 1;
				return 0;
			}

	2.分别编译模型:
		编译器会为我们跟踪相关的模板定义。但是必须让编译器直到要记住给定的模板定义,可以使用关键字export来做这件事情。
		export关键字能够指明给定的定义可能会需要在其他文件中产生实例化。
		一般在函数模板的定义中指明函数模板为export的,这是通过在关键字template之前包含export关键字而实现的,例如:
			export template <typename Type> Type sum(Type t1,Type T2){}
		对于类模板使用export,应该在类的实现文件中使用export,而不是在头文件中使用。
			//头文件
			template <typename Type>class Queue{..};
			//类定义源文件
			#include "Queue.h"
			export template <typename Type>class Queue{}
		export类的成员自动声明为export的。也可以将类模板的个别成员声明为export的,只要把关键字export不再类模板本身指定,而是只在要export的特定成员上指定。

类模板的名字:(1)独立于模板形参的那些名字,(2)依赖于模板形参的那些名字
	设计者的责任:保证所有不依赖于模板形参的名字在模板本身的作用域中定义。
	模板用户的责任:保证与用来实例化模板的类型相关的所有函数、类型和操作符的声明可见。即实例化类模板的成员或函数模板的时候,用户必须保证这些声明是可见的。	
*/

/*
//Queue类接口
template <class Type> class Queue{
public:
        Queue();
        Type &front();
        const Type &front () const;
        void push(const Type &);
        void pop();
        bool empty() const;
};*/
/*
类模板中的友元声明:
	1.普通非模板或函数的友元声明,将友元关系授予明确指定的类或函数。
	2.类模板或函数模板的友元声明,授予对友元所有实例的访问权
	3.值授予对类模板或函数模板的特定实例访问权的友元声明。
	
普通友元,例如:
	template <class Type> class Bar{
		//类Foobar和函数fcn都可以访问Bar的任意实例的private成员
		friend class Foobar;
		friend void fcn();
	};
一般模板友元关系:
	template <class Type> class Bar{
		//Foo1和temp1_fcn1的所有实例都是友元
		template<class Type> friend class Foo1;
		template<class Type> friend void temp1_fcn1(const Type&);
	};
特定的模板友元关系:
	template<class T> class Foo2;
	template<class T> void temp1_fcn2(const T&);
	template<class T> class Bar{
		//只有形参为char*的Foo2和temp1_fcn2的特定实例可以访问Bar的每个实例。
		friend class Foo2<char*>;
		friend void temp1_fcn2<char*>(char* const &);
	};

	更一般的特定模板友元关系:
	template<class T> class Foo3;
	template<class T> void temp1_fcn3(const T&);
	template<class Type> class Bar{
		//只有与给定Bar实例有相同模板实参的那些Foo3和temp1_fcn3版本是友元。
		friend class Foo3<Type>;
		friend void temp1_fcn3<Type>(const Type&);
	};	

*/
#include<iostream>
using namespace std;

//声明Queue类
template<class Type> class Queue;
//队列的节点类
template<class Type> class QueueItem{
public:
	//将Queue设为友元
	friend class Queue<Type>;
	QueueItem(const Type &t):item(t),next(0){}
	Type getItem(){
		return item;
	}
	QueueItem* getNext(){
		return next;
	}
private:
	//数据
	Type item;
	//指向下一个元素的指针
	QueueItem<Type>* next;
};

//队列类
template <class Type> class Queue{
public:
	//默认构造器
	Queue():head(0),tail(0){}
	//复制构造器
	Queue(const Queue &Q):head(0),tail(0){
		copy_elems(Q);
	}
	//析构函数,当调用析够函数时,删除所有的元素释放内存
	~Queue(){destroy();}
	//返回头元素
	Type& front(){
		return head->item;
	}
	//返回头元素的const对象
	const Type &front() const{
		return head->item;
	}
	//声明入队函数
	void push(const Type &);
	//声明出队函数
	void pop();
	//判断是否为空的函数
	bool empty() const{
		return head==0;
	}
	QueueItem<Type> *head;
	QueueItem<Type> *tail;
	
private:
	//删除所有元素
	void destroy();
	//根据传入的参数,复制实参的元素
	void copy_elems(const Queue&);

};
/*
在类体外定义类模板成员函数:
	1.必须以关键字template开头,后接类的模板形参表。
	2.必须指出它是哪个类的成员。
	3.类名必须包含其模板参数,例如
		template<class T> ret-type className<T>::member-name
*/
//destroy函数
template<class Type> void Queue<Type>::destroy(){
	while(!empty())
		pop();

}
//pop函数
template<class Type> void Queue<Type>::pop(){
	QueueItem<Type>* p=head;
	head=head->next;
	//删除该节点
	delete p;
}
//push函数
template<class Type> void Queue<Type>::push(const Type &val){
	QueueItem<Type> *pt=new QueueItem<Type>(val);
	if(empty()){
		head=tail=pt;
	}
	else{
		//指针指向下一个元素
		tail->next=pt;
		//修改末尾节点	
		tail=pt;
	}
}
//copy_elems函数
template<class Type> void Queue<Type>::copy_elems(const Queue &orig){
	//遍历该容器的节点
	for(QueueItem<Type> *pt=orig.head;pt;pt=pt->next){
		//把没个节点的数据都push到当前对象	
		push(pt->item);
	}
}


	//重载输出操作符号
template<class Type> ostream& operator<<(ostream &os,const Queue<Type> &q){
	os<<"<";
	QueueItem<Type> *p;
	for(p=q.head;p;p=p->getNext()){
		os<<p->getItem()<<" ";
	}
	os<<">";
	return os;
}


int main(){
	Queue<int> test;
	for(size_t i=1;i!=10;++i){
		test.push(i);
	}
	cout<<test<<endl;
	return 0;
}


 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值