類模板學習參考書籍:王健偉《C++新經典:模板與泛型編程》
創新互聯網站建設服務商,為中小企業提供成都網站建設、網站設計服務,網站設計,網站托管維護等一站式綜合服務型公司,專業打造企業形象網站,讓您在眾多競爭對手中脫穎而出創新互聯。
和函數模板一樣,類模板可以理解為產生類的模具,通過給定的模板參數生成具體的類。vector容器就是一個類模板應用的例子,vector可以存放不同類型的數據類型元素,其就是通過引入類模板來減少不同類型元素存儲時重復的代碼,代碼更加精簡和通用。示例如下:
#includeusing namespace std;
template//標識符為T的模板參數,表示myvector容器所保存的元素類型
class myvector
{public:
typedef T* myiterator; // 迭代器
public:
myvector(); //構造函數
myvector(T tmpt) // 帶參數的構造函數
{
}
myvector& operator=(const myvector&); // 重載賦值運算符,在類模板中使用模板名可以不用提供模板參數,如myvectorpublic:
void myfunc()
{cout<< "mufunc() 被調用"<< endl;
}
public:
// 迭代器接口
myiterator mybegin(); //迭代器起始位置
myiterator myend(); //迭代器結束位置
};
templatemyvector::myvector() // 類外構造函數實現
{}
int main()
{myvectortempvec; //T被替換成int,即指定模板參數T為int
myvector tempvec1(6); // 不用指定模板參數
tempvec.myfunc(); //調用類模板中的普通成員函數
}
對于類模板myvector,myvector稱為類名或類模板,myvectormyvector& operator=(const myvector&);
,但在類模板外不可以,如myvector
。
對于類模板的模板參數推導,代碼myvector tempvec1(6);
中,我們通過調用含參數的構造函數實例化模板類,編譯器通過傳入的實參類型可以自動推導出T的類型。而myvector
則是通過類名<類型>
指定T的類型(倘若沒有帶參數的構造函數,T仍需指定)。實現模板參數推導的功能是通過使用推斷指南(deduction guide),其作用為推斷類模板參數時提供推斷指引。一般我們常見的(如上面代碼中的例子)都為隱式推斷指南,無需指定(前提是待傳入參數的構造函數存在)編譯器自動推斷。當然,程序員也可以自定義推斷指南(如不存在構造函數的情況)。形式如下:
templateA(T,T)->A;
對于類模板的特化。類模板的全特化如下:
template<>class myvector{public:
myvector()
{cout<< "全特化版本"<< endl;
}
void myfunc();
};
void myvector::myfunc()
{cout<< "全特化版本的myfunc"<< endl;
}
int main()
{myvectortempvec;
tempvec.myfunc();
}
需要區分的是,泛化版本的類模板和全特化版本的類模板只是同名,兩者實例化后的對象是完全不同的兩個類,即成員屬性和函數無法共享。其次對在全特化版本類模板外定義的成員函數不能在開頭加template<>
,相當于全特化后變成了一個普通類。
普通成員函數和靜態變量的全特化如下:
#includeusing namespace std;
template//標識符為T的模板參數,表示myvector容器所保存的元素類型
class myvector
{public:
typedef T* myiterator; // 迭代器
static int m_stc; // 靜態變量聲明
public:
myvector(); //構造函數
myvector(T tmpt) // 帶參數的構造函數
{}
myvector& operator=(const myvector&); // 重載賦值運算符,在類模板中使用模板名可以不用提供模板參數,如myvectorpublic:
void myfunc();
public:
// 迭代器接口
myiterator mybegin(); //迭代器起始位置
myiterator myend(); //迭代器結束位置
};
templatemyvector::myvector() // 類外構造函數實現
{cout<< "泛化版本的構造函數被調用"<< endl;
}
template<>class myvector{public:
myvector()
{cout<< "全特化版本"<< endl;
}
void myfunc();
};
void myvector::myfunc()
{cout<< "全特化版本的myfunc"<< endl;
}
templatevoid myvector::myfunc()
{cout<< "泛化版本的普通成員函數myfunc()被調用"<< endl;
}
template<>void myvector::myfunc()
{cout<< "泛化版本的普通成員函數myfunc()的全特化版本被調用"<< endl;
}
templateint myvector::m_stc = 10;
template<>int myvector::m_stc = 100;
int main()
{myvectortempvec;
tempvec.myfunc();
cout<< tempvec.m_stc<< endl;
}
在上述代碼的mian函數中,我們使用myvector
指令模板參數類型為float實例化了類模板,因為myvector類模板存在全特化版本,所以編譯器會先找到全特化版本,但是由于全特化版本中模板參數特化類型為int,class myvector
,所以只能使用泛化版本,因此運行的是泛化版本的構造函數。接著,運行代碼tempvec.myfunc();
,tempvec是泛化版本示例化后的類,所以myfunc
函數也是泛化版本中的,不過在泛化版本中存在全特化的同名函數,所以會優先選擇其。不過由于該成員函數的全特化版本模板參數類型為double,void myvector
,不是該實例化類中模板參數float,所以該全特化函數不會取代原來的同名成員函數。對于模板類中的靜態變量m_stc同理。亦然,我們如果最開始指定模板類型為double,那么會依次執行泛化版本的構造函數,泛化版本的全特化myfunc成員函數,輸出泛化版本的m_stc靜態變量。想一想,如果實例化類的模板參數類型為int,那么cout<< tempvec.m_stc<< endl;
必然會報錯,因為類模板的全特化版本中沒有這一靜態變量。
注意:如果進行了普通成員函數或靜態成員變量的全特化,那么就無法用這些全特化時指定的類型對整個類模板進行全特化了。因為在對成員函數或靜態成員變量進行了全特化后導致實例化了對應類型的類模板,如果再次進行全特化,將不會重復進行相同類型的實例化,編譯器報錯。
對于類模板的偏特化有兩種:一是模板參數數量上的偏特化;一是模板參數范圍上的偏特化。具體實現和原理類似于函數模板的偏特化。
對于默認參數:
myvector<>tmpvec
。template
。類型別名,可以通過typedef或者using關鍵字給類型名起一個別名。
typedef TCIF_TC;
using IF_TCU = TC;
和函數模板一樣,類模板中同樣也可以有非類型模板參數,但全局指針、浮點數和字符串常量不能作為非類型模板參數。
成員函數模板示例如下:
#includeusing namespace std;
templateclass A
{public:
A(double v1, double v2) // 普通構造函數
{cout<< "A::A(double,double)執行了!"<< endl;
}
A(T1 v1, T1 v2) // 使用類模板參數類型的構造函數
{cout<< "A::A(T1,T1)執行了!"<< endl;
}
templateA(T2 v1, T2 v2); // 構造函數模板
templatevoid myfunc(T3 tmpt) // 普通成員函模板
{cout<< tmpt<< endl;
}
T1 m_ic;
static constexpr int m_stcvalue = 200;
};
// 在類外實現類模板的構造函數模板
template// 先寫類模板的模板參數列表
template// 再寫構造函數模板自己的模板參數列表
A::A(T2 v1, T2 v3)
{cout<< "A::A(T2,T2)執行了!"<< endl;
}
int main()
{Aa(1, 2);
a.myfunc(3);
}
拷貝構造函數模板不等同且永遠不可能成為拷貝構造函數,拷貝賦值運算符模板不等同且永遠不可能成為拷貝賦值運算符。類型相同的對象拷貝構造調用的是拷貝構造函數,類型不同的對象拷貝構造調用的是拷貝構造函數模板,并不會因為找不到對應調用對象而且調用另一個。
對于成員函數模板也具有特化版本。
類模板嵌套相關資料表示,C++標準不允許在類模板之外全特化一個未被全特化的類模板的成員函數模板。即在類模板外,如果要全特化一個成員函數模板,需要確保該成員函數模板所屬的類模板為全特化版本。
類模板中套類模板和類中類相差不大。需要注意的是,將子類模板的成員函數寫在父類模板的泛化版本之外,應當如下形式:
template//父類模板參數列表
template//子類模板參數列表
void A::B::myfunc()
{}
變量模板與成員變量模板變量模板定義和使用如下:
#includeusing namespace std;
templateT myvar{}; // 變量模板,{}為零初始化
int main()
{myvar= 13;
myvar= 13.1;
cout<< myvar<< " "<< myvar<< endl;
}
不同的指定類型得到不同的變量。
templateT myvar{}; // 泛化版本
template<>char myvar{}; // 全特化版本,myvar可以當作char類型使用
templateT myvar{120}; // 偏特化版本
templateT myvar{}; // 默認模板參數
templateT myvar[val]; // 非類型模板參數
templateclass A
{public:
templatestatic W m_tpi; // 成員變量模板
};
// 成員變量模板在類模板外定義
templatetemplateW A::m_tpi = 5;
別名模板與成員別名模板別名模板的作用主要是簡化書寫。
#include
模板模板參數之前我們學習的模板參數有類型模板參數和非類型模板參數,這一部分將提出模板模板參數,即模板參數本身為模板,將類模板當作參數傳遞到另一個模板中。我們在學習類模板的時候知道vector、list等容器類其實也是類模板,所以若我們想要將這些容器類作為模板參數傳入模板中,寫法如下:
#include#include#includeusing namespace std;
templateclass Container = std::vector>//templatetypename Container = std::vector>class myclass
{public:
Containermyc;
public:
void func();
myclass()
{for (int i = 0; i< 10; i++)
{ myc.push_back(i); // 本行代碼正確性取決于模板參數類型
}
}
};
templateclass Container>void myclass::func()
{cout<< "mstifiy"<< endl;
}
int main()
{myclassmylistobj; // double是容器中的元素類型,list是容器類型
mylistobj.func();
std::cout<< mylistobj.myc.size()<< endl;
}
其中重點注意模板參數列表的寫法:
templateclass Container = std::vector>templatetypename Container = std::vector>// class也能替換成typename修飾
templatetypename Container = std::vector>// W為容器模板的類型模板參數,不能用,故省略
templatetypename Container>// 沒有默認模板參數
class和typename可以相互替代,都是合法的。
你是否還在尋找穩定的海外服務器提供商?創新互聯www.cdcxhl.cn海外機房具備T級流量清洗系統配攻擊溯源,準確流量調度確保服務器高可用性,企業級服務器適合批量采購,新人活動首月15元起,快前往官網查看詳情吧
本文名稱:【C++泛型學習筆記】類模板、變量模板和別名模板-創新互聯
本文網址:http://vcdvsql.cn/article46/isceg.html
成都網站建設公司_創新互聯,為您提供微信公眾號、用戶體驗、企業建站、定制開發、品牌網站制作、網站設計
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯