一、類模闆與模闆類
- 類模闆:一個模闆(是模闆)
- 模闆類:調用類模闆生成的類對象(是類實體),也稱為類模闆的執行個體化
類模闆的定義:
- 與函數模闆的定義是一樣的
- template <typename T>
- class Blob
- {
- public:
- Blob();
- Blob(std::initializer_list<T> i);
- };
模闆類的使用:
- 在定義類時,使用到類名的地方都需要顯示的給出模闆類的類型,格式為<>
- int main()
- {
- Blob<int> ia;
- Blob<int> ia2 = { 1,2,3 };
- Blob<double>* ia4 = new Blob<double>{ 1.1,3.14 };
- Blob<string> ia3 = { "Hello","World" };
- return 0;
- }
二、模闆類的成員函數
- 如果模闆類的成員函數在類内聲明,而在類外定義,需要遵循以下規則:在函數前也在加上模闆清單,且類名限定符後面給出<>
- template <typename T>
- class Blob
- {
- public:
- Blob();
- Blob(std::initializer_list<T> i);
- T func(T const &str);//在類内聲明
- };
- //類外定義
- template <typename T>
- T Blob<T>::func(T const &str)
- {
- }
類模闆中使用其它模闆類型
- template <typename T> class Blob{
- template <typename It> Blob(It b, It e);//構造函數的參數使用其它模闆類型
- };
- template <typename T>
- template <typename It>
- Blob<T>::Blob(It b, It e):data(std::make_shared<std::vector<T>>(b,e))
- {
- }
- int main()
- {
- vector<long> vi = { 0,1,2 };
- list<const char*> w = { "Hello","World" };
- Blob<int> a1(vi.begin(), vi.end());
- Blob<string> a2(w.begin(), w.end());
- return 0;
- }
三、友元:類模闆中的友元
- 一個類模闆中也可以擁有友元(友元類/友元函數)
- 下面隻有當與Blob類型相同的BlobPtr類和operator==函數才可以成為Blob模闆類的友元
- template <typename T> class BlobPtr;
- template <typename T> class Blob;
- template <typename T>
- bool operator==(const Blob<T>&, const Blob<T>&)
- {
- }
- template <typename T> class Blob
- {
- friend class BlobPtr<T>; //使BlobPtr模闆類成為Blob模闆類的友元
- friend bool operator==(const Blob<T>&, const Blob<T>&);//使operator函數成為Blob模闆類的友元
- };
- template <typename T> class BlobPtr
- {
- };
四、友元:通用和特定的模闆友元關系
- 模闆有需要複雜的關系,下面列出兩個執行個體
- template <typename T> class Pal;
- class C
- {
- friend class Pal<C>; //用類C執行個體化的Pal是C的友元
- template <typename T> friend class Pal2; //Pal2的所有執行個體都是C的友元
- };
- template <typename T> class C2
- {
- friend class Pal<T>; //與C2相同類型的執行個體化Pal才是C2的友元
- template <typename X> friend class Pal2;//任何類型執行個體化的Pal2對象都是C2的友元,因為模闆參數清單不同
- friend class Pal3;//Pal3是一個非模闆類,它是所有類型C2執行個體化的友元
- };
五、類模闆的static成員
- 與任何其他類一樣,類模闆可以聲明static成員
- 例如:下面Foo類模闆中定義了一個static函數和static變量
- template <typename T> class Foo
- {
- public:
- static std : size_t count() { return ctr; }
- private:
- static std::size_t ctr;
- };
- 因為類的static成員變量隻可在類内定義,在類外初始化。是以模闆來的static變量也要在類外初始化,初始化時需要加上模闆參數清單,例如下面代碼,當一個特定的模闆執行個體化Foo時,其ctr被初始化為0
- template <typename T>
- std::size_t Foo<T>::ctr = 0; //定義并初始化
- 靜态成員的調用
- Foo<int> fi; //執行個體化Foo<int>類和static資料成員ctr
- auto ct=Foo<int>::count();//執行個體化Foo<int>::count
- ct=fi.count(); //使用Foo<int>::count,與上面的意義是相同
- ct=Foo::count(); //錯誤,Foo沒有指出使用哪個模闆執行個體化
- 類模闆的static成員的特點:當一個類給出模闆執行個體化之後,與這個類執行個體化類型相同的類共享一樣的靜态成員
Foo<int> f1,f2,f3; //f1,f2,f3共享Foo<int>::count()和Foo<int>::str
六、使用類的類型成員(::符号)
引入:
string::size_type //編譯器知道我們要通路string類中的size_type資料類型
- 當我們通過作用域符通路的名字是類型還是static成員,編譯器會自動識别,例如:
- 但是對于模闆就不能使用這種方法了,例如:
- //編譯器不知道size_type是一個static資料成員還是一種資料類型,是以産生二義性
- T::size_type * p;
- 預設情況下,C++語言假定通過作用域運算符通路的名字不是資料類型,而是資料成員。是以如果我們希望使用一個模闆類型參數的類型成員,就必須顯式地告訴編譯器改名字是一個類型。需要通過typename來實作這一點
- 例如下面的top函數:
- template<typename T>
- typename T::value_type top(const T&c)
- {
- if (!c.empty())
- return c.back();
- else
- return typename T::value_type();
- }
七、成員模闆
- 一個類可以包含模闆類型的成員函數,這種成員稱為“成員模闆”
- 注意:成員模闆不能為虛函數
①普通(非模闆)類的成員模闆
- 概念:我們可以在一個非模闆類中定義一個成員模闆
示範案例
- 預設的情況下,unique_ptr會調用元素的析構函數來删除元素。下面我們定義了一個删除器,删除器使用operator()接收一個元素指針,并将該元素進行delete
- //函數對象類,對給定指針執行delete
- class DebugDelete
- {
- public:
- DebugDelete(std::ostream &s=std::cerr):os(s){} //構造函數
- //根據傳入的參數進行delete
- template <typename T>
- void operator()(T* p)const //成員模闆
- {
- os << "deleting unique_ptr" << endl;
- delete p;
- }
- private:
- std::ostream &os;
- };
- 下面是基本的使用格式:
- int main()
- {
- double *p = new double;
- DebugDelete d;
- d(p); //調用DebugDelete::operator()(double*)釋放p
- int *ip = new int;
- DebugDelete()(ip); //在一個臨時DebugDelete對象上調用operator()(int*)
- return 0;
- }
- 下面我們将這個類作為unique_ptr的删除器來使用
- int main()
- {
- //一個類型為int的unique_ptr對象,DebugDelete作為其删除器
- unique_ptr<int, DebugDelete> p(new int, DebugDelete());
- //一個類型為string的unique_ptr對象,DebugDelete作為其删除器
- unique_ptr<std::string, DebugDelete> sp(new std::string, DebugDelete());
- return 0;
- }
②類模闆的成員模闆
- 概念:對于類模闆,我們也可以為其定義成員模闆。在此情況下,類和成員各自有自己的、獨立的模闆參數
示範案例
- 例如下面Blob是一個類模闆,模闆類型為T資料成員vector的類型也為T。另外其構造函數也是一個模闆,其接受的模闆類型為It
- template<typename T>
- class Blob {
- public:
- template<typename It>
- Blob(It b, It e); //構造函數接受一個疊代器區間,用來初始化data
- private:
- std::vector<T> data;
- };
- 現在我們在類的外部定義構造函數,由于類模闆與成員函數都是模闆,是以在外部定義時需要分别同時給出這兩個模闆的模闆參數清單
- 執行個體化成員模闆:為了執行個體化一個類模闆的成員模闆,我們必須同時提供類和函數模闆的實參。見下面的示範案例,其中:
- a1:Blob的類型為int,構造函數的類型為int*
- a2:Blob的類型為int,構造函數的類型為vector<long>::iterator
- a3:Blob的類型為string,構造函數的類型為list<const char*>::iterator