C++中模板類和模板類的友元函數


http://www.cnblogs.com/assemble8086/archive/2011/10/02/2198308.html有詳細介紹,下面只講什么時候模板類會實例化以及模板類中成員函數實例化和友元函數的使用。

模板類的使用實現了編譯時多態性,避免了運行時多態性的消耗(虛指針和虛函數表),但是編譯時多態和運行時多態並不沖突,編譯時多態是根據傳入模板的對象類型實現不同的操作完成,比如繼承同一接口的不同類型可以在編譯時根據調用接口的隊形類型實現多態,而運行時多態則根據虛函數來實現,必須等到運行時才能判斷真正運行的接口。

當模板類中使用虛函數時,模板實例化的時候必須實例化器虛函數表,一旦虛函數表實例化就必須實例化其全部虛函數

引起模板類的實例化的主要情形有以下幾種:

在我們使用類模板時,只有當代碼中使用了類模板的一個實例的名字,而且上下文環境要求必須存在類的定義時,這個類模板才被實例化。並不是每次使用一個類都要求知道該類的定義。

(1)聲明一個類模板的指針和引用(不是初始化或者new時),不會引起類模板的實例化,因為沒有必要知道該類的定義。例如:

  1. <span style="font-family:'Microsoft YaHei';">class Matrix;  
  2. Matrix *pm;//不需要類的定義  
  3.   
  4. void inverse(Matrix &);//也不需要類的定義</span>  

以及

  1. <span style="font-family:'Microsoft YaHei';">void foo(Queue<int> &qi)  //引用聲明
  2. {  
  3.     Queue<int> *pqi = &qi;  //不會實例化
  4.     //...  
  5. }</span>  

 

但是如果檢查這個指針或引用所值的那個對象時,類模板才會被實例化。比如在上例中,如果指針pqi被解引用,qi被用來獲得它所指向的對象值,或者pqi或qi被用來訪問Queue<int>的數據成員或成員函數時,Queue<int>才會被實例化。

  1. <span style="font-family:'Microsoft YaHei';">void foo(Queue<int> &qi)  
  2. {  
  3.     Queue<int> *pqi = &qi;  
  4.   
  5.     //因為成員函數被調用,所以Queue<int>被實例化  
  6.     pqi ->add(255);  
  7.     //...  
  8. }</span>  

(2)定義一個類類型的對象時需要該類的定義,因此類模板會被實例化、例如:

  1. <span style="font-family:'Microsoft YaHei';">class Matrix;  
  2. Matrix obj1;//Error  
  3.   
  4. class Matrix{...};  
  5. Matrix obj1;//OK</span>  

下面的例子中,對象qi的定義引起類模板Queue<int>被實例化:

 

  1. <span style="font-family:'Microsoft YaHei';">Queue<int> qi;</span>  


(3)在使用sizeof()時,它是計算對象的大小,編譯器必須根據類型將其實例化出來,所以類模板被實例化:

 

  1. <span style="font-family:'Microsoft YaHei';">int iobj = sizeof(Stack<string>);</span>  

 

(4)new表達式要求類模板被實例化,或者引用初始化的時候實例化。

Queue<int> *p_qi = new Queue<int>;

 

(5)引用類模板中的成員會導致類模板被編譯器實例化。

 

(6)需要注意的是,類模板的成員函數本身也是一個模板標准C++要求這樣的成員函數只有在被調用或者取地址的時候,才被實例化。(在標准C++之前有些編譯器在實例化類模板時,就實例化類模板的成員函數。)用來實例化成員函數的類型,就是其成員函數要調用的那個類對象的類型。

(7)類模板中的友元模板函數是非成員函數,當然類模板的實例化不會影響友元模板函數的實例化,只有調用友元模板函數的時候才會實例化,要保證參數類型嚴格相符,否則找不到定義,除非友元模板函數是inline函數

(8)在模板類未實例化之前,編譯器不會對任何類成員的類型進行檢查,定義聲明使用不是該模板類的成員編譯器也不會報錯,也就是說,模板類並不知道自己究竟擁有多少成員擁有什么成員,只有實例化后才會進行檢查。

----------------------------------------------------------------------------------------------------------------------------------------------------------

模板類的友元分3類:
非模板友元
約束(bound)模板友元,即友元的類型取決於類被實例化時的類型。
非約束(undound)模板友元,即友元的所有具體化都是類的每一個具體化的友元。

 

1.模板類的非模板友元函數

template<class T>
class HasFriend
{ friend void counts();  //friend to all HaFriend instantiations
  ... };

上訴聲明使counts()函數成為模板所有實例化的友元。


     counts()函數不是通過對象調用的,也沒有對象參數。它可以訪問全局對象;可以使用全局指針訪問非全局對象;可以創建自己的對象;可以訪問獨立於對象的模板類的靜態數據成員。

 

為友元函數提供模板類參數,必須提供特定的具體化,如HasFriend<short>。

要提供模板類參數,必須指明具體化:
template<class T>
class HasFriend
{ friend void report(HasFriend<T> & ); 
 // bound template friend
  ... };

 

     也就是說,帶HasFriend<int>參數的report()將成為HasFriend<int>類的友元函數。同樣,帶HasFriend<double>參數的report()將是report()的一個重載版本——它將是HasFriend<double>類的友元。

 

report()本省並不是模板函數,而只是使用一個模板作參數。這意味着必須為要使用的友元定義顯示具體化:
void report(HasFriend<short> & ) {...} //explicit specialization for short
void report(HasFriend<int> & ) {...}     //explicit specialization for int

 

2.模板類的約束模板友元函數
修改前一個上面的范例,使友元函數本身成為模板。使類的每一個具體化都獲得與友元匹配的具體化。包含以下三步:

首先,在類定義的前面聲明每個模板函數:
template <typename T> void counts();
template <typename T> void report(T &);

 

然后在函數中再次將模板聲明為友元。這些語句根據類模板參數的類型聲明具體化:
template<class TT>
class HasFriend
{ friend void counts<TT>();
  friend void report<>(HasFriend<TT> &);
  ... };

 

    上述聲明中的<>指出這是模板具體化。對於report(),<>可以為空,這是因為可以從函數參數推斷出模板類型參數(HasFriend<TT>)。不過,也可以使用report<HasFriend<TT> >(HasFriend<TT> &)。但counts()函數沒有參數,因此必須使用模板參數句法(<TT>)來指明其具體化。還需要注意的是,TT是HasFriend類的參數類型。

 否則鏈接失敗。

當然也可以指定類型的函數,例如 friend void report<char>(char &);

假設聲明了這樣一個對象:
HasFriend<int> squack;

則編譯器將用int替換TT,並生成下面的類定義:
class HasFriend<int>
{ friend void counts<int>();
  friend void report<>(HasFriend<int> &);
  ... };

於是,模板具體化counts<int>()和report<HasFriend<int> >()被聲明為HasFriend<int>類的友元。

 注意:類模板的實例化不會實例化一個友元函數,只是聲明友元而不實例化

最后,程序必須為友元提供模板定義有兩種方式,

一種在類內聲明定義,另一種先聲明然后再類外定義。例如:
template <typename T>
void counts() { cout << HasFriend<T>::x << endl;}

template <typename T>
void report(T & hf) {cout << hf.x << endl;}

 該方式為類外定義。

而類內聲明定義不用在類前聲明模板函數,而是直接在類內定義:

template<class TT>
class HasFriend
{ friend void counts(){...}

  friend void report(HasFriend<TT> &){...}
  ... };

注意:這里沒有<>,因為這表示友元函數是模板函數,這種情況下,友元函數隨着模板類實例化而實例化(inline)

調用時,counts()需要提供具體化參數類型。report()只需直接調用,例如:
counts<int>();  //調用HasFriend<int>的友元函數
HasFriend<double> hfdb;
report(hfdb);   //調用HasFriend<double>的友元函數,此時實例化友元函數。

 

3.模板類的非約束模板友元函數
    通過在類內部聲明友元函數模板,可以創建非約束友元函數,即每個函數具體化都是每個類具體化的友元:
template <typename T>
class ManyFriend
{...
 template <typename C,typename D> friend void show(C &,D &);
};

 

在類外定義該友元:
template <typename C,typename D>
void show(C & c,D & d){ ... }

 

假如創建ManyFriend<int>類對象(hfi1)和ManyFriend<double>類對象(hfi2),傳遞給show(),那么編譯器將生成如下具體化定義:
void show<ManyFriend<int> &,ManyFriend<double> &> (ManyFriend<int> & c,ManyFriend<double> & d){ ... }//使用時才實例化

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM