前面我們介紹了函數(shù)模板。今天我們來看看C++的另一種泛型:類模板。C++中類模板通常是容器(如std::vector)或行為的封裝(如之前我們實(shí)現(xiàn)的chan<T>類)。類模板語法:
template < parameter-list > class-declaration
構(gòu)成類模板的形參(parameter-list)約束與函數(shù)模板相同,此處就不贅述了。與函數(shù)模板的類型自動推演不同,類模板實(shí)例化時,需要顯式指定:
std::vector<int> intArr;
一、成員函數(shù)
與函數(shù)模板一樣,類模板只是定義了一組通用的操作,在具體實(shí)例化前是不占用程序空間的。這種Lazy特性在類模板中得到了進(jìn)一步地加強(qiáng):成員函數(shù)(含成員函數(shù)模板)只有在使用時才生成。
template<typename T>
class A {
T a_;
public:
void add(int n) {
a_ += n;
}
};
class M{};
int main() {
A<M> s; // s未用到add函數(shù),因此整個程序得以成功編譯
// s.add(1); // 如果有這句話,則會編譯失敗
}
本例中,A<T>::add在變量s中未使用到,因此雖然a_ += n不合法,但整個程序仍然通過了編譯。
1.1 虛函數(shù)
在函數(shù)模板中我們提到虛函數(shù)不能是函數(shù)模板,那么在類模板中可以有虛函數(shù)嗎?答案是肯定的,類模板中可以有虛函數(shù),虛函數(shù)在類模板實(shí)例化為模板類時由編譯器生成,因此其實(shí)現(xiàn)必須是合法的,否則即使未被使用到,也會編譯失敗。類模板的虛函數(shù)可以訪問模板類中的泛型成員(變量、成員函數(shù)模板都可以訪問)。
#include <iostream>
template<typename T>
class A {
T a_;
public:
virtual void say() {
std::cout << "a -> " << a_ << std::endl;
}
};
class M{};
int main() {
// 盡管say函數(shù)未被使用,此處會編譯仍會失敗,因?yàn)閟td::cout << m.a_操作是非法的
A<M> m;
}
1.2 成員函數(shù)模板
類模板和函數(shù)模板結(jié)合就是成員函數(shù)模板。
#include <iostream>
template<typename T>
class Printer {
T prefix_;
public:
explicit Printer(const T &prefix):prefix_(prefix){
}
// 成員函數(shù)模板
template<typename U, typename ...Args> void print(const U &u, Args... args);
void print() {
std::cout << std::endl;
}
};
template<typename T> template<typename U, typename ...Args>
void Printer<T>::print(const U &u, Args... args) {
std::cout << this->prefix_ << u << std::endl;
print(args...);
}
二、類模板特化與偏特化
模板特化是指定類模板的特定實(shí)現(xiàn)。是針對某類型參數(shù)的特殊化處理。假設(shè)我們有一個類模板Stack<T>,它有一個功能:min(取Stack的最小值),則該類模板的典型實(shí)現(xiàn)如下:
template<typename T>
struct StackItem {
StackItem *next;
T item;
};
template<typename T>
class Stack {
StackItem<T> *front = nullptr;
public:
T min() const {
assert(front != nullptr);
T min = front->item;
for (StackItem<T> *it = front->next; it != nullptr; it = it->next) {
if (it->item < min) {
min = it->item;
}
}
return min;
}
};
Stack<T>::min所需滿足的契約是:T需支持小于操作(operator <)。但有些類型無法滿足該要求,如const char *。如果Stack<T>要支持const char *的話,則需要特化。
template<>
class Stack<const char *> // 類名后面,跟上<...>,則表明是特化
{
StackItem<const char *> *front = nullptr;
public:
const char * min() const {
assert(front != nullptr);
const char * min = front->item;
for (StackItem<const char *> *it = front->next; it != nullptr; it = it->next) {
if (strcmp(it->item, min) < 0) {
min = it->item;
}
}
return min;
}
};
2.1 偏特化
偏特化也叫部分特化,指的是當(dāng)類模板有一個以上模板參數(shù)時,我們希望能對某個或某幾個模板實(shí)參進(jìn)行特化。類模板的特化(或偏特化)只需要模板名稱相同并且特化列表<>中的參數(shù)個數(shù)與原始模板對應(yīng)上即可,模板參數(shù)列表不必與原始模板相同模板名稱相同。一個類模板可以有多個特化,與函數(shù)模板相同,編譯器會自動實(shí)例化那個最特殊的版本。
完全特化的結(jié)果是一個實(shí)際的class,而偏特化的結(jié)果是另外一個同名的模板。
三、類模板中的static成員
類模板中可以聲明static成員。但需要注意的是每個不同模板實(shí)例都會有一個獨(dú)立的static成員變量。
template<typename T>
class A {
public:
static int count;
};
template<typename T> int A<T>::count = 1;
則A<int>::count
與A<double>::count
是不同的兩個變量。
3.1 類模板中static成員的特化
static成員也可以進(jìn)行特化
// template<typename T> A {...}; // 見上面的定義
template<> int A<const char *>::count = 100;
則A<const char *>::count的值被值始化為100,而以其它類型進(jìn)行實(shí)例化時則初始化為1。
四、友元
友元在C++中做為一個BUG式的存在,可以授權(quán)“好友”訪問其隱私數(shù)據(jù)。
template<typename T> class A; // 前置聲明,在B中聲明友元需要的
template<typename U>
class B {
// 每個B的實(shí)例將授權(quán)相同類型實(shí)例化的A
friend class A<U>;
// C的所有實(shí)例都是B的友元,該種情況下C無需前置聲明
template<typename T> fiend class C;
// 普通類
friend class D;
// 模板自己的類型參數(shù)成為友元
friend U;
};
五、總結(jié)
本節(jié)簡單介紹了類模板,由于篇幅限制不能一一展開,如有疏漏歡迎批評指正。
上一篇 C++11多線程-函數(shù)模板 |
目錄 | 下一篇 |
---|