C++的模板機制

爲何須要模板編程

所謂函數模板,其實是創建一個通用函數其函數返回類型、形參類型或函數中使用的類型不具體指定,用一個虛擬的類型來表明,這個通用函數就稱爲函數模板ios

所謂類模板,和函數模板差很少,創建一個通用類,其類中的成員類型和類中使用的類型不具體指定,用一個虛擬的類型表明編程

簡單的說,無論是函數仍是類,除了數據的類型不一樣,其餘都同樣的函數或者類,咱們經過用一個或多個虛擬類型來表明不一樣的類型,創建一個通用函數(模板函數)或類(模板類)函數

再簡單的講,模板編程就是類型參數化,即參數模板this

函數模板

//template 關鍵字告訴C++編譯器 我要開始泛型了.你不要隨便報錯  
//數據類型T 數據類型參數化
template <typename T> 
void swapT(T &a,T &b)
{
	T temp = a;
	a = b;
	b = temp;
}

函數模板的調用方式編碼

一、顯示調用    如:swapT<int>(a,b)spa

二、自動數據類型推導    如:swapT(a,b)  編譯器會根據實參的類型自動推導出模板的參數類型.net

自動推導需注意虛擬的數據類型T不在函數的形參中出現時,必須經過顯示調用來告訴編譯器應該把你的虛擬數據類型轉化爲具體的類型。code

函數模板與普通函數重載的調用規則對象

一、函數模板遵循嚴格的參數類型匹配,而普通函數能夠經過隱式的參數類型轉化來匹配調用函數blog

二、當調用的函數參數類型與普通函數參數類型徹底匹配(沒有通過隱式的類型轉化),就調用普通函數

三、當調用的函數參數類型經過隱式類型轉化才與普通函數類型匹配,而調用的函數參數類型與模板的類型一致,就調用模板函數

簡單的說:優先調用普通函數(其沒有隱式類型轉化),其次纔是模板函數(普通函數參數隱式類型轉化了)

類模板

 

注意:一、當將類模板的.h和.cpp分開後,須要在包含main函數的文件中#include<xxx.cpp>

            二、不要濫用友元函數,不然會形成嚴重錯誤。能夠使用友元函數實現左移右移運算符的重載(在模板類中聲明做左移友元函數時要加上<T>

eg:   friend ostream& operator<< <T>(ostream& out,Complex& comx);)

單個類模板 (調用必須顯示實例化類)

代碼例子

#include <iostream>
using namespace std;

/*定義模板類,類的類型參數化*/
template<class T>
class B 
{
public:
	B(T b)
	{
		this->b = b;
	}
	void printB()
	{
		cout<<"B b:"<<this->b<<endl;
	}
	protected:
	T b;
};

int main()
{
	B<int> bb(33);   //顯示參數類型化,二次編譯時告訴編譯器要給類B分配的內存
	bb.printB();
    system("pause");
	return 0
}

模板類派生普通類(繼承模板類時須要顯示參數類型化

代碼例子

#include <iostream>
using namespace std;

/*定義模板類,類的類型參數化*/
template<class T>  /*能夠使用class,也能夠使用typename*/
class B 
{
public:
	B(T b)
	{
		this->b = b;
	}

	void printB()
	{
		cout<<"B b:"<<this->b<<endl; 
    }
protected:
	T b;          /*將模板類的成員類型參數化*/
};

/*類模板派生出普通類*/
class C :public B<int>  //!!!派生普通類,基類的參數必須類型化,告訴編譯器基類的佔用內存
{
public:
	C(int b,int c):B(b)
	{
		this->c = c;
	}
	void printC()
	{
		cout<<"B b:"<<this->b<<"  C c:"<<this->c<<endl;
	}
private:
	int c;
};

int main()
{
    //調用派生的普通類
	C c(12,34);
	c.printC();
    system("pause");
	return 0;
}

模板類派生模板類(在實例化對象時,須要顯示參數類型化)
代碼例子

#include <iostream>
using namespace std;

/*定義模板類,類的類型參數化*/
template<class T> //能夠是class,也能夠是typename
class B 
{
public:
	B(T b)
	{
		this->b = b;
	}
	void printB()
	{
		cout<<"B b:"<<this->b<<endl;
	}
protected:
	T b;
};

/*模板類派生模板類*/
template<class T1,class T2>
class D : public B    //!!!派生模板類能夠不具體參數類型
{
public:
	D(T1 b,T2 d):B(b)
	{
		this->d = d;
	}
	void printD()
	{
		cout<<"B b:"<<this->b<<"  D d:"<<this->d<<endl;
	}
private:
	T2 d;	
};

int main()
{
    /*實例化派生的模板類*/
	D<int,char> d(55,'h');	//必須使用顯示參數類型化方式
	d.printD();
	system("pause");
	return 0;
}

模板函數和類編譯本質

編譯器並非把函數模板處理成可以處理任意類的函數
編譯器從函數模板經過具體類型產生不一樣的函數

模板函數和類編譯實現

編譯器會對函數模板進行兩次編譯

一、在聲明的地方對模板代碼自己進行編譯;(對其進行詞法、語法、句法的分析)
二、在調用的地方對參數替換後的代碼進行編譯。(生成具體的函數(或類))

練習

一、編碼模板函數並調用,利用g++編譯成彙編文件,觀察兩次編譯實現

二、編碼模板類並實例,利用g++編譯成彙編文件,觀察兩次編譯實現

三、模板類派生普通類和模板類並實例,利用g++編譯成彙編文件,觀察兩次編譯實現

編譯命令及過程,參考此文http://my.oschina.net/u/1783725/blog/680722

但願能是你們對模板機制有了深刻的瞭解,若有疑問,可留言共同探討。哈哈

相關文章
相關標籤/搜索