[TOC]
# 一、引言
在写排序算法时,如果要写一个交换两个数据的函数,由于基本数据类型有int、float、double等等类型,所以针对每种数据类型可能我们要写很多swap函数,这些函数除了传入的数据类型不同,函数体结构大致相同。所以C++为了避免让程序员写很多大量重复代码,设计了一种叫做“模板”的东西。我们写程序时,先不指定什么类型,在调用时我们再说明一下是什么类型,具体怎么实现接着往下看。
# 二、函数模板
## 1、定义
像开头所说,如果要对int、double类型的两个数进行交换我们要写两个函数,但用函数模板时只需要写一个函数即可。模板定义如下:
`template <typename T>`
或者
`template <class T>`
其中,template是声明一个模板,typename是声明一个虚类型T,这个T可以代替int、double等基本数据类型,那为什么还有class?因为最初是用class声明一个T来表示通用数据类型,但考虑到class会和“类”混淆,就改用typename进行定义,同时保留了class,这两者效果相同,但我个人比较习惯用class。
在进行声明模板之后下面就开始写模板的函数体了,例如交换函数,合起来就是:
~~~java
template <class T>
void swap(T *p1,T *p2){
T temp=*p1;
*p1=*p2;
*p2=temp;
}
~~~
这样就是一个完整的函数模板
## 2、调用
有两种方式使用模板
(1)自动类型推导:编译器会自动判定你传入的数据是什么数据类型,然后将T改成对应数据类型进行操作;
(2)自己声明数据类型进行调用,具体实现如下:
~~~java
//1、自动类型推导
swap(a, b);
cout << "a=" << a << endl;
cout << "b=" << b << endl;
//2、显示指定类型
swap<int>(a, b);
cout << "a=" << a << endl;
cout << "b=" << b << endl;
~~~
**何时必须自己声明数据类型呢?**
**那就是没有参数传递的时候**。比如知识一个打印函数,没有参数传递,这里就不写代码了。
## 3、多个虚类型
前面是针对一个函数里面都是同一数据类型的,如果含有不同数据类型,假如有两个:
~~~
template<class T1,class T2>
void func(T1 a,T2 b){....}
~~~
调用时可以自动识别类型也可以自己声明数据类型:
~~~
func<int,double>(1,3.14);
~~~
# 三、类模板
前面已经介绍了模板大致的作用,这里对类模板就不做过多说明,直接上干货:
## 1、定义
我们定一个学生类
~~~java
template<class T1,class T2,class T3>
class Student{
public:
Student(T1 name,T2 age,T3 score){
..........
}
T1 m_Name;
T2 m_Age;
T3 m_Score;
}
~~~
## 2、调用
**调用时必须指定输入了什么数据,也就是尖括号不能省略,这点与函数模板不一样**
~~~
Student<string,int,float>s("Tom",18,85.5);
~~~
# 四、类的函数模板
在类内定义的话就跟前面的函数模板一样,**如果在类外定义,类外也要写上函数模板的形式:**
~~~java
template<class numType>
class Compare{
public:
Compare(numType a,numType b){
this->a=a;
this->b=b;
}
//声明模板函数:
numType max( );
private:
numType a;
numTypr b;
}
//类外定义模板函数
template<class numType>
numType Compare::max( ){
return this->a>this->b?this->a:this->b;
}
~~~
# 五、类作为数据类型传入
~~~java
//定义Person1
class Person1 {
public:
void showPerson1() {
cout << "Person1 show" << endl;
}
};
//定义Person2
class Person2 {
public:
void showPerson2() {
cout << "Person2 show" << endl;
}
};
//定义类模板
template<class T>
class MyClass {
public:
T obj;
//类模板中的成员函数
void func1() {
obj.showPerson1();
}
void func2() {
obj.showPerson2();
}
};
//主函数
int main() {
MyClass<Person1>m;
m.func1();
//m.func2();//会报错,因为“showPerson2”: 不是“Person1”的成员
system("pause");
return 0;
}
~~~
# 六、类模板与继承
如果父类是一个模板,子类继承父类模板的时候,不知道父类模板内存是多少,编译器就无法给子类分配内存,解决方案是给子类也加上模板,方案如下:
~~~java
template<class T>
class Base {
T m_Name;
};
//class Son :public Base { //错误,必须知道T的内存才能指定空间
class Son1:public Base<int>{ // 不灵活
};
//想要灵活指定父类中T的类型
template<class T1,class T2>
class Son2 :public Base<T2> {
public:
T1 m_A;
};
int main() {
//让子类的T为string,子类的T1为int
Son2<int, string>s2;
system("pause");
return 0;
}
~~~
# 七、类模板与友元
前面也有过**传入类对象到函数里面,如果这个函数需要用到对象里面的数据,而该数据有被设定为private(私有)就无法直接访问**,此时又要用到友元函数的知识:
**注意:类内添加友元函数后,如果没有对该友元函数进行声明,编译器认为你没有这个函数,会进行报错。**
~~~java
//先声明类和函数,防止编译器报错
template<class T1,class T2>
class Person;
template<class T1, class T2>
void printPerson(Person<T1, T2> p);
template<class T1,class T2>
class Person {
//全局函数类外实现的声明
friend void printPerson<>(Person<T1, T2> p);
public:
Person(T1 name, T2 age) {
this->m_Name = name;
this->m_Age = age;
}
private:
T1 m_Name;
T2 m_Age;
};
//全局函数类外实现
template<class T1,class T2>
void printPerson(Person<T1, T2> p) {
cout << "Name:" << p.m_Name << endl;
cout << "Age:" << p.m_Age << endl;
}
~~~
觉得有用记得顶一下哦\_
- C++基础
- 什么是 POD 数据类型?
- 面向对象三大特性五大原则
- 低耦合高内聚
- C++类型转换
- c++仿函数
- C++仿函数了解一下?
- C++对象内存模型
- C++11新特性
- 智能指针
- 动手实现C++的智能指针
- C++ 智能指针 shared_ptr 详解与示例
- 现代 C++:一文读懂智能指针
- Lamda
- c++11多线程
- std::thread
- std::async
- std::promise
- std::future
- C++11 的内存模型
- 初始化列表
- std::bind
- std::tuple
- auto自动类型推导
- 可变参数模板
- 右值引用与移动语义
- 完美转发
- 基于范围的for循环
- C++11之POD类型
- std::enable_if
- C++14/17
- C++20
- 协成
- 模块
- Ranges
- Boost
- boost::circular_buffer
- 使用Boost.Asio编写通信程序
- Boost.Asio C++ 网络编程
- 模板
- 模板特化/偏特化
- C++模板、类模板、函数模板详解都在这里了
- 泛化之美--C++11可变模版参数的妙用
- 模板元编程
- 这是我见过最好的模板元编程文章!