模版分成函数模版和类模版
模板的精神其实很简单:参数化类型。 换句话说, 把一个原本特定于某个类型的算法或类当中的类型信息抽掉,抽出来 做成模板参数 T。
函数模版
所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不 具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。
语法格式:
template<typename T>
template<class T>
这两个哪个都行
多类型参数的话:
template<typename 类型参数表>
返回类型 函数模板名(函数参数列表) {
函数模板定义体
}
如:template<class t1,class t2...>
template 是语义是模板的意思,尖括号中先写关键字 typename 或是 class ,后 面跟一个类型 T,此类即是虚拟的类型。
#include <iostream>
using namespace std;
template<typename T> //一个template下只能有一个函数
void myswap(T &a, T &b)
{
T t = a;
a = b;
b = t;
}
int main(void)
{
int x=1;
int y=2;
myswap(x, y);
cout <<"x: " << x <<", y:" << y <<endl;
myswap<int>(x, y);
cout <<"x: " << x <<", y:" << y <<endl;
char a = 'a';
char b = 'b';
myswap(a, b); //自动类型推导
cout <<"a: " << a <<", b:" << b <<endl;
myswap<char>(a, b); //显式的指定类型
cout <<"a: " << a <<", b:" << b <<endl;
return 0;
}
函数模板,只适用于函数的参数个数相同而类型不同,且函数体相同的情况。 如果个数不同,则不能用函数模板。
函数模版和普通函数的区别__函数模版调用规则
函数模版和普通函数的区别:
函数模版不允许自动类型转化
普通函数能够进行自动类型转化
#include <iostream>
using namespace std;
template <typename T>
void myswap(T &a, T &b)
{
T t;
t = a;
a = b;
b = t;
cout<<"myswap 模板函数do"<<endl;
}
void myswap(char a, int b)
{
int t;
t = a;
a = b;
b = t;
cout<<"myswap 普通函数do"<<endl;
}
int main() {
char cData = 'a';
int iData= 2;
myswap(cData, iData);
myswap(iData, cData);
//普通函数会进行隐式的数据类型转换,但这时函数参数列表不能引用
myswap<int>(cData, iData);
// 函数模板不提供隐式的数据类型转换 必须是严格的匹配
return 0;
}
调用优先顺序:
- 当函数模板和普通函数都符合调用时,优先选择普通函数
- 若显示使用函数模板,则使用<> 类型列表。
如:myswap<>(a,b); - 如果 函数模板产生更好的匹配 使用函数模板
编译器对模板机制剖析
编译器编译过程:

g++ -E hello.c -o hello.i(预处理)
g++ -S hello.i -o hello.s(编译)
g++ -c hello.s -o hello.o(汇编)
g++ hello.o -o hello(链接)
以上四个步骤,可合成一个步骤 g++ hello.c -o hello(直接编译链接成可执行目标文件)

编译器并不是把函数模板处理成能够处理任意类的函数
编译器从函数模板通过具体类型产生不同的函数
比如上图:三次调用Myadd,实际上只会从函数模版产生两种模版函数编译器会对函数模板进行两次编译,在声明的地方对模板代码本身进行编译; 在调用的地方对参数替换后的代码进行编译。
先对函数模版编译一次,然后对产生的模版函数再编译一次。
类模版
类模版基本语法:
template<typename T>
class A
{
}
与函数模版不同的是:
函数模版在调用时可以自动类型推导
而类模版必须显式指定类型
如:A<int> a();
类模版派生普通类_类模版派生类模版
template<class T>
class A
{
public:
A(T a) {
this->a = a;
}
protected:
T a;
};
class B : public A<int>{...} //派生普通类必须要显式指定类型
class B : public A<T>{...} //派生类模版尖括号内填T
类模版的类内实现和类外实现
类内实现是函数体写在类中:
template <class T>
class Complex
{
friend ostream & operator<<(ostream &os, Complex &c)
{
os << "( " << c.a << " + " << c.b << "i" << " )";
return os;
}
public:
Complex()
{ }
Complex(T a, T b)
{
this->a = a;
this->b = b; }
void printComplex()
{
cout << "( " << a << " + " << b << "i" << " )" << endl;
}
Complex operator+(Complex &another)
{
Complex temp(a +another.a, b +another.b);
return temp;
}
private:
T a;
T b;
};
友元函数类内实现是默认全局的。
类模版的类外实现
普通函数的类外实现
包括友元函数的类外实现的注意事项
#include <iostream>
using namespace std;
template <class T> //
class Complex;
template <class T> //
ostream& operator<<(ostream &os, Complex<T> &c);
template <class T>
class Complex
{
public:
friend ostream& operator<< <T>(ostream &os, Complex<T> &c);
//下面的类外定义是模版函数,如果这里不加<>,则代表普通函数,在链接时会出错,因为找不到普通函数的定义
//加了之后就是跟编译器说明这是模版函数
//同时友元函数类外实现时,需要在这个类前面声明这个函数
//滥用友元函数,本来可以当成员函数,却要用友元函数
Complex<T> mySub (Complex<T> &one, Complex<T> &another);
//最终的结论, 模板类 不要轻易写友元函数。但重载<<和>>必须要用到友元
Complex();
Complex(T a, T b); //普通函数,在类内不加<>的函数都是普通函数
Complex operator+(Complex &another);
Complex operator-(Complex &another);
void printComplex();
private:
T a;
T b;
};
template <class T>
Complex<T>::Complex()
{
}
template <class T>
Complex<T>::Complex(T a, T b)
{
this->a = a;
this->b = b;
}
template <class T>
void Complex<T>::printComplex()
{
cout << "( " << a << " + " << b << "i" << " )" << endl;
}
template <class T>
Complex<T> Complex<T>::operator+(Complex<T> &another)
{
Complex temp(a + another.a, b + another.b);
return temp;
}
template <class T>
Complex<T> Complex<T>::operator-(Complex<T> &another)
{
Complex temp(this->a - another.a, this->b = another.b);
return temp;
}
//友元函数
template <class T>
ostream & operator<< (ostream &os, Complex<T> &c) {
os << "( " << c.a << " + " << c.b << "i" << " )";
return os;
}
template <class T>
Complex<T> Complex<T>::mySub(Complex<T> &one, Complex<T> &another)
{
Complex<T> temp(one.a - another.a, one.b - another.b);
return temp;
}
int main(void)
{
Complex<int> a(10, 20); //让模板类具体化是为了告诉编译具体的大小,分配内存
Complex<int> b(3, 4);
a.printComplex();
Complex<int> c;
c = a + b;
c.printComplex();
cout << c << endl;
c = c.mySub(a, b);
cout << c << endl;
return 0;
}
类模版h和cpp分离编写分析
类模版声明写.h中,实现写.cpp中,在main.cpp中调用这个类模版,编译会错误,显示无法解析的外部命令,也就是发生链接错误。
原因:编译main.cpp中声明的.h编译时没有确定类的具体实现,在main函数内调用类模版的构造函数时编译器没有发现有这个函数的声明。于是就标记个符号,再让链接器去其它文件找。找到类模版的.cpp文件时,因为类模版要编译两次,第一次只是只是编译器的词法校验和分析,第二次并没有进行,所以没有生成具体的类。于是乎产生了错误。
解决方法:引入hpp文件。类模版.h声明,类模版.hpp写实现方法。
在main.cpp中。#include “模版.hpp”
之所以这样是为了让别人知道这是模版。增强可读性
类模版中的static成员
#include <iostream>
using namespace std;
template <class T>
class A{
public:
static T s_value;
};
template <class T>
T A<T>::s_value=60;
int main(){
A<int> a,b;
A<int> e;
A<char> c,d;
cout<<a.s_value<<" "<<b.s_value<<" "<<e.s_value<<endl;
cout<<c.s_value<<" "<<d.s_value<<endl;
a.s_value++;
cout<<a.s_value<<" "<<b.s_value<<" "<<e.s_value<<endl;
cout<<c.s_value<<" "<<d.s_value<<endl;
return 0;
}
运行结果:
60 60 60
< <
61 61 61
< <
类模版实例化出A<int>和A<char> ,这两个类的static成员互不干涉。
但同属于类A<int> 的对象共享static成员
ps :void add(int &&a) 能对右值取引用,此时只能 add(3);
void add(int & a) 此时只能,a=3,add(a)
但是void add(const &a)可以解决两种情况,因为const &a 可以接受常量和变量
容器都是值寓意而非引用寓意,向容器中放入元素,都是放入元素的拷贝