[C++系列]終於有一篇完整的函數模板,類模板,特化及模板分離編譯歸納總結了

在這裏插入圖片描述

前言

衆所周知使用函數重載雖然可以實現一個較爲通用的函數,但其僅僅只是類型不同,代碼的可複用率是比較低的,只要有新的類型出現,就需要增加對應的函數,代碼的可維護性也是比較低的,一個出錯可能所有的重載都會出現錯誤,因此我們也是引入了模板的概念來幫助我們解決這樣的問題。

1.泛型編程

編寫與類型無關的通用代碼,是代碼複用的一種手段,而模板則是泛型編程的基礎。

template < typename / class 泛型參數1typename / class泛型參數2.....>
{
	函數定義
}

泛型編程並沒有減少實際的代碼量,只是把重複的代碼交給機器自動生產,減少開發人員重複的工作量,極大的提高工作效率。

2. 函數模板及特化

2.1函數模板

函數模板本身並不是函數,是將本來應該我們做的事情交給編譯器去做。
在這裏插入圖片描述

template <class T>
void Swap(T& left, T& right)
{
	T temp = left;
	left = right;
	right = temp;
}

template <class T>
T add(T a, T b)
{
	return a + b;
}

template <class T1,class T2>
T2 Add(const T1& a, const T2& b)
{
	return a + b;
}

int Add(const int& a, const int& b)
{
	return a + b;
}

  • 普通函數與模板函數共存時,如果普通函數的參數類型可以完全匹配,則執行普通函數,不進行函數模板的實例化,顧名思義就是喫現有的飯,不去考慮還沒有做好的飯。
  • 如果普通函數和模板函數共存,普通函數參數類型不完全匹配,而實例化函數可以完全匹配,則進行模板的實例化。
  • 若普通函數和模板函數共存,指定了需要實例化Add<int>(a,b),則進行實例化。

2.2 函數模板的特化

函數模板
template <class T>
T Add(T& a, T& b)
{
	return a + b;
}

函數模板的特化
template <>
char* Add<char*>(char*& a, char*& b)
{
	strcat(a, b);
	return a;
}

普通函數
char* Add(char*& a, char*& b)
{
	strcat(a, b);
	return a;
}
  • 對於函數模板不能夠處理的特殊類型,一般會定義一個此類型的普通函數,函數模板你的特殊會比較少用
template <class T, size_t N>
class Array
{
private:
	T _array[N];
};
  • 模板參數可分爲:類型模板參數(class, typename), 非類型模板參數(數值類型)
  • 非類型模板參數: 可以作爲常量使用, 它的值需要在編譯時確定

3. 類模板及特化

3.1 類模板

template <class T1, class T2, class T3>
class Date
{
public:
	Date(T1 year, T2 month, T3 day)
		:_year(year)
		, _month(month)
		, _day(day)
	{}

	void Display();
	/*{
		cout << _year << "-" << _month << "-" << _day << endl;
	}*/
private:
	T1 _year;
	T2 _month;
	T3 _day;
};
template <class T1, class T2, class T3>
void Date<T1, T2, T3>::Display()
{
	cout << _year << "-" << _month << "-" << _day << endl;
}
  • 如果在類外定義類模板的成員函數,需要加上泛型的聲明,作用域爲“類名<泛型參數>”
  • 類模板不能進行隱式實例化,需要在類模板名字後跟 <>

3.2 類模板的特化

特化之前需要存在基礎類模板
template <class T1, class T2>
class A
{
public:
	A()
	{
		cout << "A(T1, T2)" << endl;
	}
private:
	T1 _t1;
	T2 _t2;
};

全特化--> 所有的參數都爲具體類型
template <>
class A<int, char>
{
public:
	A()
	{
		cout << "A(int, char)" << endl;
	}
private:
	int _t1;
	char _t2;
};



偏特化: a. 部分特化
template <class T1>
class A<T1, double>
{
public:
	A()
	{
		cout << "A(T1, double)" << endl;
	}
private:
	T1 _t1;
	double _t2;
};

template <class T1>
class A<T1, char>
{
public:
	A()
	{
		cout << "A(T1, char)" << endl;
	}
private:
	T1 _t1;
	double _t2;
};

b. 對模板參數做進一步的限制
template <class T1, class T2>
class A<T1&, T2&>
{
public:
	A()
	{
		cout << "A(T1&, T2&>" << endl;
	}
};

4. 模板分離編譯

一個程序由若干個源文件共同實現,每個源文件單獨編譯生成目標文件,最後將所有的目標文件鏈接起來形成單一可執行文件的過程稱之爲分離編譯模式。

a.h
template<class T>
bool IsEqual(const T& a, const T& b);

a.cpp
template<class T>
bool IsEqual(const T& a, const T& b)
{
	return a==b;
}

main.cpp
#include"a.h"
int main()
{
	IsEqual(1, 1);
	IsEqual(1.0, 2.0);
	return 0;

在這裏插入圖片描述
因此如果需要對上方所出現的問題進行解決的話,有兩種方法:

  1. 將聲明和定義放到一個文件“xxx.h”裏面,推薦使用這種
  2. 模板定義的時候使用顯式實例化,但是不實用。

5. 模板總結

在這裏插入圖片描述
【優點】

  1. 模板複用了代碼,節省資源,更快的迭代開發,C++的標準模板庫(STL)因此而產生
  2. 增強了代碼的靈活性
    【缺陷】
  3. 模板會導致代碼膨脹問題,也會導致編譯時間變長
  4. 出現模板編譯錯誤時,錯誤信息非常凌亂,不易定位錯誤
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章