C++primer学习笔记(4)

第十五天

函数模板

template 后接模板形参表

template <typename T>
int compare(const T &v1,const T &v2)
{
	if(v1<v2) return -1;
	if(v2<v1) return 1;
	return 0;
}

函数形参报定义了特定类型的局部变量但并不初始化那些变量,在运行时来初始化形参。

template <typedef T> inline T min(const T&,const T&);
类模板

以关键字template开头,后接模板形参列表。
Queue模板接受一个名为Type的模板类型形参。

template <class Type> class Queue
{
public:
	Queue();
	Tyep &front();
	const Type &front() const;

	void push(const Type &);
	void pop();
	bool empty() const;
private:
	//...
};
模板形参

给模板形参富裕的唯一含义是区别形参是类型形参还是非类型形参:

  • 类型形参:该形参表示的是未知类型
  • 非类型形参:是一个未知值

模板形参会遵循常规的名字屏蔽规则。

用作模板形参的名字不能在模板内部重用。并且同意模板形参的名字只能在同一模板形参表中使用一次。
模板类型形参前必须带上关键字class或typename

类型成员

必须显式指定为类型。否则默认为数据成员。

template <class Parm,class U>
Parm fcn(Parm* array,U value>
{
	typename Parm::size_type *p;
}

给用实例化fcn的类型早呢更加了一个指责:那些类型个必须具有名为size_type的成员,并且是一个类型。

非类型模板形参
template <class T,size_t N> void array_init(T (&parm)[N])
{
	for(size_t i = 0;i!=N;++i)
	{
		parm[i] = 0;
	}
}

array_init是一个含有一个类型模板形参和一个非类型模板形参的函数模板。
函数本身接受一个数组的引用形参。

对实参类型的要求尽可能少是很有益的。

  • 模板的形参是const引用
  • 函数中的测试只用<比较 可以减少对函数的要求
实例化

编译器用模板产生指定的类或函数的特定类型版本。产生版本的特定类型实例的过程称为实例化。

Queue<int> qi;

==

template <class Type> class Queue<int>
{
public:
	Queue();
	int &front();
	const int &front() const;
	void push(const int &);
	void pop();
	bool empty() const;
private:
	//....
};
类型形参的实参的受限转换 typename的调用

如果要允许实参的常规转换,则函数必须用两个类型形参来定义。

short si;

template <typename T,typename R>
int compare(const T &v1,const T &v2)
{
	if(v1<v2) return -1;
	if(v2<v1) return 1;
	return 0;
}

编译器只会执行两种转换:

  1. const转化:就收const引用或const指针的函数可以分别用非const对象的引用或指针来调用,无需产生新的实例化。如果函数接受非引用类型,形参类型实参都忽略const。
  2. 数组或函数到指针的转换:如果模板不是引用类型,则对数组或函数类型的实参应用常规指针转换。数组实参将当做指向其第一个元素的指针,函数实参当做指向函数类型的指针。
template<typename T> T fobj(T,T);
template<typename T) T fref(const T&,const T&);

string s1("a value");
const string s2 ("another value");

fobj(s1,s2);//const 忽略
fref(s1,s2);//s1转化成const

int a[10],b[42];
fobj(a,b);// f(int *,int *);
fref(a,b);//error 引用不能转化成指针
模板实参推断和函数指针
template<typename T> int compare(const T&,const T&);

int (*pf1)(const int &,const int&) = compare;

指针pf1,指向 接受两个const int& 类型形参并返回int值的函数。

函数模板的显式形参 覆盖模板实参推断机制

希望定义名为sum、接受两个不同类型实参的函数模板,希望返回类型足够大。

  1. 强制sum的调用者将较小的类型强制转换为希望用作结果使用的类型
int i;short s;
sum(stract_cast<int>(s),i);
  1. 引入第三个模板形参
template<class T1,class T2,class T3);
T1 sum(T2,T3);
  1. 显式模板实参
long val3= sum<long>(i,lng);

显式模板实参可以消除二义性

template<typename T> int compare(const T&,const T&);

void func(int(*)(const string&,const string&));
void func(int(*)(const int&,const int&));
func(compare<int>);
模板编译模型

将类定义和函数声明放在头文件中。而普通函数和类成员函数的定义放在源文件中。
编译模板:类定义和函数声明放在头文件中,函数定义和成员函数放在源文件中。
所有编译器支持“包含模型”,只有一些编译器支持“分别编译模型”。

包含编译模型
编译器必须看到所有模板的定义。 #include<>
分别编译模型
让编译器知道要记住给定模板定义。export
指明给定的定义可能会需要在其他文件中产生实例化。

在这里插入图片描述

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

template <class Type>
class Queue
{
    //默认构造函数
    Queue() : head(0), tail(0);
    //复制构造函数 初始化head 和tail 并调用函数复制元素
    Queue(const Queue &Q) : head(0), tail(0)
    {
        copy_elems(Q);
    }
    Queue &operator=(const Queue &);
    ~Queue() { 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;
    }

private:
    QueueItem<Type> *head;
    QueueItem<Type> *tail;
    void destory();
    void copy_elems(const Queue &);
};

template <class Type>
void Queue<Type>::destory()
{
    while (!empty)
    {
        pop();
    }
}
template <class Type>
void Queue<Type>::pop()
{
    QueueItem<Type> *p = head;
    head = head->next;
    delete p;
}

template <class Type>
void Queue<Type>::push(const Type &val)
{
    //分配新的QueueItem对象 用传递的值初始化
    QueueItem<Type> *pt = new QueueItem<Type>(val);
    if (empty())
    {
        head = tail = pt;
    }
    else
    {
        tail->next = pt;
        tail = pt;
    }
}

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

类模板中的友元声明
  1. 普通非模板类或函数的友元声明:将友元关系授予明确指定的类或函数
  2. 类模板或函数模板的友元声明:授予对友元所有实例的访问权。
template <class T> friend class Fool;
temolate<class T>friend void temp1(const T&);
  1. 指收于对类模板或函数模板的特定实例的访问权的友元声明。友元声明前必须声明类或函数。
friend class Foo2<char*>; //error 未声明
friend void temp1<char*> (char* const &); //error 未声明 
成员模板

任意类可以拥有本身为类模板或函数模板的成员,这种成员称为成员模板。
例如标准容器的assign成员,接受两个迭代器的assign版本使用模板形参表示其迭代器形参的类型。

定义模板成员

template <class Type> class Queue
{
public:
	template<class It>
	Queue(It beg,It end):head(0),tail(0) {copy_elems(beg,end);}
	template<class Iter> void assign(Iter,Iter);
private:
	template<class Iter> void copy_elems(Iter,Iter);
};

成员函数的开关是自己的模板形参表。

template<class T> template<class Iter>
void Queue<T>::assign(Iter beg,Iter end)
{
	destory();
	copy_elems(beg,end);
}

成员和模板醉熏常规访问控制。

类模板的static成员
template<class T> class Foo
{
public:
	static std::size_t count(){return ctr;}
private:
	static std::size_t ctd;
};

Foo类的每个实例化都有自己的静态成员。
Foo类型的任意对象共享同一个static成员ctr.
Foo类型的对象共享另一个不同的ctr成员。

泛型句柄类

提供管理使用计数和基础对象的操作。

模板特化

略…

后面高级特性。。。P858

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

老李头带你看世界

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

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

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

打赏作者

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

抵扣说明:

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

余额充值