[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可變模版參數的妙用
- 模板元編程
- 這是我見過最好的模板元編程文章!