C++学习笔记:16章 泛型编程

16.1.5 非类型模板形参
模板形参不必都是类型。
template <class T, size_t N> void array_init(T (&arr)[N])
{
	cout << "N = " << N << endl;
	for(size_t i=0; i!=N; i++)
		arr[i] = 0;
}
N不是类型模板形参。
调用:
int a[30];
array_init(a);
则参数N为30.

16.1.6 编写泛型程序
使用模板函数产生的程序是否合法,取决于函数中使用的操作以及所用类型支持的操作。
程序员需要保证函数实参的类型支持这些操作。但最好在编写模板代码时,对实参类型的要求尽可能的少。

16.2 实例化
编译器使用模板产生制定的类或者函数的特定类型的版本,叫做实例化。

要点:
实例化是在编译过程中完成的。
类模板在引用实际模板类型时实例化;函数模板在调用或者用它对函数指针赋值的时候实例化。

注意,对于模板函数,通过函数实参的类型,来获取模板类型实参和实例化的时候,类型必须严格匹配。在这个匹配的过程中,不会做默认类型转换的动作的。
比如一个模板有一个实参T:
template <typename T> int compare(const T &v1, const T &v2)
{
	if(v1 > v2) return 1;
	if(v1 == v2) return 0;

	return -1;
}

当调用如下:
int main()
{
	short a = 10;
	int b = 11;
	compare(a , b);
}
会报错:error: no matching function for call to ‘compare(int&, short int&)’

如果希望使用内部类型的默认转换,需要在模板函数中定义两个类型形参。
template <typename T1, typename T2> int compare(const T1 &v1, const T2 &v2)
{
	if(v1 > v2) return 1;
	if(v1 == v2) return 0;

	return -1;
}

====>支持的转换仅限两种:
const 转换:形参为const引用或指针 ,可接受 非const对象的引用或指针的实参。
数组或函数到指针的转换:数组当作指向第一个元素的指针;函数实参当作指向函数类型的指针。

注意:这些类型转换的限制,只对类型为模板类型形参的模板函数 —— 实例化之后的转换不受限制。


可以使用模板函数对函数指针赋值初始化。


16.2.2 函数模板的显式实参
template <typename T1, typename T2, typename T3> 
T1 compare(const T2 &v1, const T3 &v2)
{
	return v1 + v2;
}

对于这个例子,我们无法确定返回值类型。
比如:求和的函数,接受int  short  long等类型的实参,如何确定返回值呢?
这里需要用到显式类型实参来调用:
	int m = 100;
	double n  =21.5;
	long x = sum_1<long>(m,n); //指定其返回值类型为long


16.3 模板编译模型
普通函数调用,只要有函数声明即可,模板函数需要在实例化的时候产生实例,所以必须能够访问函数的源代码.

类类型的定义也是一样。普通类类型定义,成员函数可以只在头文件里声明,函数的实现可以放在cpp源文件里。
但模板类的成员函数在实例化的时候,必须访问到源码。

不同的编译器支持不同的处理方式。两种:
1 包含编译模型.
在源文件里包含模板函数或者模板类的头文件。需要编译器支持。

2 分别编译模型。
头文件中定义这些模板的类和函数的实现。
源文件中,通过export这些声明。


16.4 类模板成员
在使用类的时候,必须指定模板类的类型形参。
但在类本身的作用域内部,可以使用类模板的非限定名字。比如在模板类Queue<Type>的定义体中,可以直接使用Queue 来代替Queue<type>。
但是在类体外定义其他的成员函数,必须显式指明类型形参。——我觉得没必要省了这个,类内外都显示指明吧。


1 类模板成员函数
形式如下:
必须以关键字template开头,后接类的模板形参表
必须指出类的作用域,类名+作用域操作符
类名必须包含模板形参
template <class T> return-type Queue<T>::member-name
例如:
template <class Type> void Queue<Type>::push(const Type &item)
{
	cout << "push: " << item << endl;
}
用名为Type的类型形参定义一个函数模板;
返回oid;
在类模板Queue<Type>的作用域中。
然后是成员函数名,函数参数列表。


类模板的成员函数只有为程序所用的时候才会实例化,否则不会实例化。
由参与调用类成员函数的对象的类型来确定成员函数的模板形参。


16.4.2 非类型形参的模板实参
可以使用非类型形参,以Screen类为例:
template <int hi, int wid> class Screen{
public:
	Screen():screen(hi*wid, '#'),cursor(0), height(hi), width(wid) {
		cout << "new screen1 :width= "<< width << "\theight= "<< height << endl;
	}

	Screen(char c):screen(hi*wid, c),cursor(0), height(hi), width(wid) {
		cout << "new screen2 :width= "<< width << "\theight= "<< height << endl;
	}
private:
	string screen;
	string::size_type cursor;
	string::size_type height, width;
};

//---------------------------------------------------------
16.4.3 类模板中的友元声明
三种友元声明,声明了与一个或多个实体的友元关系
1 普通非模板类或函数的友元声明:与一个类或函数的友元关系。
	friend class xxx;
2 类模板或函数模板的友元声明:声明对友元所有实例的访问权。
	template <class T> friend class Queue;
3 只授予对类模板或函数模板特定实例的访问权。
	template <class Type> class Queue;
	friend class Queue<int>;  //限定了具体类型
	或
	//最常用:不限定类型,但友元类的类型形参必须与当前模板类的类型形参相同,各自具体的实例一一对应组成友元关系。
	//先声明函数
	template <class Type> class Queue;
	template <class Type> ostream& operator<<(ostream &os, const Queue<Type> &q); 
	//在类体中声明友元
	friend class Queue<Type>; 
	friend ostream& operator<< <Type>(ostream &os, const Queue<Type> &q); 


注意模板函数的友元声明,在函数名后面要接类型形参。比如:
对于函数
template <class Type> void frd_func(const Queue<Type> &q)
{
	cout << "frd func test: " << q.head->item << endl;
}
//需要访问两个类QueueItem<Type>和Queue<Type>的私有数据,所以要声明为类的友元。
//先声明这个函数。
template <class Type> void frd_func(const Queue<Type> & q);
//再在类体中声明友元,注意函数名后面接类型形参
friend void frd_func<Type>(const Queue<Type> & q);


4 声明的依赖性
当给定了对给定模板所有实例的访问权限之后,又不希望一些类成员被访问,则可以在声明友元之前,声明这些类成员。
也就是说,友元的作用域在声明之后的所有成员。


下面是测试代码

//C++ primer plus ...... chapter 15  page490

#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
#include <utility>
#include <new>
#include <functional>

using namespace std;

//use list to implement Queue

template <class Type> class Queue;
template <class Type> ostream& operator<<(ostream &os, const Queue<Type> &q);
template <class Type> void frd_func(const Queue<Type> & q);

//private class
template <class Type> class QueueItem
{
//public:
	//template <class T> friend class Queue;
	friend class Queue<Type>;
	friend ostream& operator<< <Type>(ostream &os, const Queue<Type> &q);
	friend void frd_func<Type>(const Queue<Type> & q);


	QueueItem(const Type &t):item(t), next(0) { }
	
	Type item;
	QueueItem *next;
};

template <class Type> class Queue
{
public:
	Queue():head(0),tail(0) { cout << "Queue constructor" << endl; }
	Queue(const Queue &q):head(0),tail(0) { 
		cout << "Queue constructor" << endl;
		copy_elems(q); 
	}
	~Queue() { 
		cout << "Queue destructor" << endl;
		destory(); 
	}

	Type& front() { return head->item; }
	const Type& front() const  { return head->item; }
	void push(const Type &);
	void pop();
	bool empty() const { return head == 0; }

	friend ostream& operator<< <Type>(ostream &os, const Queue<Type> &q);
	friend void frd_func<Type>(const Queue<Type> & q);
private:
	QueueItem<Type> *head;
	QueueItem<Type> *tail;

	void destory();
	void copy_elems(const Queue<Type>&);
};

//private functions
template <class Type> void Queue<Type>::destory()
{
	while(!empty())
		pop();
}

template <class Type> void Queue<Type>::copy_elems(const Queue<Type> &orig)
{
	for(QueueItem<Type> *p=orig.head; p ; p=p->next){
		push(p->item);
	}
}

//public functions
//push one item to the tail of queue
template <class Type> void Queue<Type>::push(const Type &item)
{
	cout << "push \t" << item << endl;

	QueueItem<Type> *p = new QueueItem<Type>(item);
	p->next = 0;
	if(empty())
		head = tail = p;
	else{
		tail->next = p;
		tail = p;
	}
}

template <class Type> void Queue<Type>::pop()
{
	QueueItem<Type> *p = head;
	head = head->next;

	cout<<"pop \t" << p->item << endl;
	delete p;
}

//operator, friend function
template <class Type> ostream& operator<<(ostream &os, const Queue<Type> &q)
{
	os << "<";
	QueueItem<Type> *p;
	for(p=q.head; p!=0; p=p->next){
		os << p->item << " ";
	}
	os << ">";

	return os;
}


template <class Type> void frd_func(const Queue<Type> &q)
{
	cout << "frd func test: " << q.head->item << endl;
}
//---------------------------------------------------------
//非类型形参的模板实参
template <int hi, int wid> class Screen{
public:
	Screen():screen(hi*wid, '#'),cursor(0), height(hi), width(wid) {
		cout << "new screen1 :width= "<< width << "\theight= "<< height << endl;
	}

	Screen(char c):screen(hi*wid, c),cursor(0), height(hi), width(wid) {
		cout << "new screen2 :width= "<< width << "\theight= "<< height << endl;
	}
private:
	string screen;
	string::size_type cursor;
	string::size_type height, width;
};


int main(int argc, char const *argv[])
{
	Queue<int> ique;
	int a = 12;
	int b = 34;
	ique.push(a);
	ique.push(b);
	ique.push(int(1231));
	cout << ique << endl;

	frd_func(ique);


	Screen<10, 20> scr1;
	Screen<100, 200> scr2('o');

	return 0;
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值