模板類的友元


非模板友元

聲明一個常規友元

template <class T>

class HasFriend

{

public:

  friend void counts();

}

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

counts()函數不是通過對象調用的(它是友元,不是成員函數),也沒有對象參數,那么它如何訪問HasFriend對象的呢

有很多種可能性。它可以訪問全局對象;可以使用全局指針訪問非全局對象;可以創建自己的對象;可以訪問獨立對象的模板類

的靜態數據成員。

如果要為友元函數提供械板類參數,可以如下所示來進行友元聲明。要提供模板類參數,必須指明基體化

template<typename T>

class HasFriend

{

  friend void report(HasFriend<T> &);

}

report()本身並不是模板函數,而只是使用一個模板作參數。這意味着必須要使用的友元定義顯式基體化:

void report(HasFriend<short> &){....}

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

也就是說report(HasFriend<int> &)將成為HasFriend<int>類的友元。

#include<iostream>

using std::cout;
using std::cin;
using std::endl;

template<typename TT>
class HasFriendT
{
private:
    TT item;
    static int ct;
public:
    HasFriendT(const TT & i):item(i){ct++;};
    ~HasFriendT(){ct--;};
    friend void counts();
    friend void report(HasFriendT<TT> &);
};

template <typename T>
int HasFriendT<T>::ct = 0;

void counts()
{
    cout<<"int count: "<<HasFriendT<int>::ct<<";";
    cout<<"double  count:"<<HasFriendT<double>::ct<<endl;
}

void report(HasFriendT<int> & hf)
{
    cout<<hf.item<<endl;
}

void report(HasFriendT<double> & hf)
{
    cout<<hf.item<<endl;
}
int main()
{
    counts();
    HasFriendT<int> hfi1(10);
    counts();
    
    HasFriendT<double> hfdb(10.5);
    report(hfi1);
    HasFriendT<int> hfi2(20);
    
    report(hfi2);
    report(hfdb);
    
    counts();
    cin.get();
}

//int count: 0;double  count:0
//int count: 1;double  count:0
//10
//20
//10.5
//int count: 2;double  count:1

 

約束模板友元函數友元的類型取決於類被實例化時的類型

可以修改上邊的示例,使友元函數本身成為模板。具體的說,為約束模板友元作准備,要使類的每一個基體

化都獲得與友元匹配的基體化。這需要3部

1首先,在類定義的前面聲明每個模板函數

template <typename T>void counts()

template <typename T> void report(T &);

2然后,在函數中再次將模板聲明為友元。這些語句根據類模板參數的類型聲明具體化:

template <typename TT>

class HasFriendT

{

  friend void counts<TT>();

  friend void report<>(HasFriendT<TT> &);

};

聲明中的<>指出這是模板具體化。對於report(),<>可以為空,因為可以從函數參數推斷出如下模板類型參數:

HasFriendT<TT>

然而也可以使用:

report<HasFriendT<TT>>(HasFriendT<TT> &)

但是counts()函數沒有參數,因此必須使用模板參數語法(<TT>)來指明其基體化。還需要注意的是,

TT是HasFriendT類的參數類型。

  同樣,理解這些聲明的最佳方式也是設想聲明一個特定具體化的對象時,它們將變成什么樣子。例如,

假設聲明了這樣一個對象

HasFriendT<int> squack;

編譯器將用int替換TT,並生成下面的類定義 

class HasFriendT<int>

{

  friend void counts<int>();

  friend void report<>(HasFriendT<int> &);

}

3第三個條件是為友元提供模板定義

看例子:

#include<iostream>

using std::cout;
using std::cin;
using std::endl;
template <typename T> void counts();
template <typename T> void report(T &);

template<typename TT>
class HasFriendT
{
private:
    TT item;
    static int ct;
public:
    HasFriendT(const TT & i):item(i){ct++;};
    ~HasFriendT(){ct--;};
    friend void counts<TT>();
    friend void report<>(HasFriendT<TT> &);
};

template <typename T>
int HasFriendT<T>::ct = 0;

template<typename T>
void counts()
{
    cout<<"template size: "<<sizeof(HasFriendT<T>)<<";";
    cout<<"template counts():"<<HasFriendT<T>::ct<<endl;
}
template<typename T>
void report(T & hf)
{
    cout<<hf.item<<endl;
}
int main()
{
    counts<int>();
    HasFriendT<int> hfi1(10);
    HasFriendT<int> hfi2(20);
    HasFriendT<double> hfdb(10.5);
    report(hfi1);
    report(hfi2);
    report(hfdb);
    cout<<"counts<int> output:\n";
    counts<int>();
    cout<<"counts<double>() output:\n";
    counts<double>();
    cin.get();
}

//template size: 4;template counts():0
//10
//20
//10.5
//counts<int> output:
//template size: 4;template counts():2
//counts<double>() output:
//template size: 8;template counts():1

counts<double> 和couts<int> 報告的模板大小不同,這樣每種T類型都有自己的友元函數count();

非約束模板友元

友元的所有具體化都是類的每一個具體化的友元

上邊說的約束模板友元函數是在類外面聲明的模板的具體化。int類型具體化獲得int函數具體化,

依此類推。通過在類內部聲明模板,可以創建非約束友元函數,即每個函數具體化都是每個類具體化的友元。

對於非約束友元,友元模板類型參數與模板類類型參數是不同的:

template <typename T>

{

  template <typename C,typename D>

  friend void Show2(C &,D &)

}

上邊是一個使用非約束友元函數的例子,其中,函數調用show2(hfi1,hfi2)與下面的具體化匹配:

void Show2<ManyFriend<int> &,ManyFriend<int> &>(ManyFriend<int> & c,ManyFriend<int> & d);

因為它是所有ManyFriend 具體化的友元,所以能夠訪問所有具體化的item成員,但它只訪問了ManyFriend<int>對象。

同樣Show2(hfd,hfd2)與下面具體化匹配:

void Show2(ManyFirend<double> &,ManyFirend<int> &>(ManyFirend<double> & c,ManyFirend<int> & d);

它也是所有ManyFriend具體化的友元,並訪問了ManyFirend<int> 對象的item成員和ManyFriend<double>對象的item成員

#include<iostream>
using std::count;
using std::cout;
using std::endl;

template<typename T>
class ManyFriend
{
private:
    T item;
public:
    ManyFriend(const T & i):item(i){};
    template<typename C,typename D>
    friend void Show2(C &,D &);
};

template<typename C,typename D>
void Show2(C & c,D & d)
{
    cout<<c.item<<","<<d.item<<endl;
}

int main()
{
    ManyFriend<int> hfi1(10);
    ManyFriend<int> hfi2(20);
    ManyFriend<double> hfdb(10.5);

    cout<<"hfi1, hfi2";
    Show2(hfi1,hfi2);
    cout<<"hfdb,hfi2:  ";

    Show2(hfdb,hfi2);
    std::cin.get();
}

//hfi1, hfi210,20
//hfdb,hfi2:  10.5,20

 

 模板別名(c++ 11)

如果能為類型指定別名,將很方便,在模板設計中尤其如此,可使用typedef 為模板具體化指定別名:

typedef std::array<double,12> arrd;

type std::array<int,12> arri;

arrd gallons;//gallons 是 std::array<double,12> 類型

arrai days;  //das 是std::array<int,12> 類型

c++11新增了使用模板提供一系列的別名,如下

template<typename T>

using arratype = std::array<T,12>;

這將arrtype定義為一個模板別名,可使用它來指定類型,如下所示:

arratype<double> gallons;  // gallons是std::array<double,12>類型

arratype<int> days;    //days 是 std::array<int ,12> 類型

總之,arrtype<T>表示類型 std::array<T,12>.

c++11 允許將語法using = 用於模板,用於非模板時,這種語法與常規typedef造價:

typedef const char * pc1;//用pc1為const char *的別名

using pc= const char * //用pc2為const char *的別名

typedef int (*pfunc)(int);//

          //定義一個有10個指針的數組,該指針指向一個函數,該函數有一個整形參數,並返回一個整型?

          //第一種方法:int (*a[10])(int);

          //第二種方法:typedef int (*pfunc)(int);

          //用法    pfunc a[10];

 uisng pc1= const int *(*)[10]//  int *(*)[10]

 上邊說的Remote方法可以影響Tv的對象,直接訪問Tv的屬性和方法。也可以通過讓類彼此成為對方的友元來實現,

即除了Remote是Tv的友元外,Tv還是Remote的友元。需要記住的一點是,對於使用Remote對象的Tv方法,其原型

可在Remote類聲明之前聲明,但必須在Remote類聲明之后定義,以便編譯器有足夠的信息來編譯該方法。這種方案與

下面類似 :

class Tv

{

  friend class Remote;

public:

  void buzz(Remote & r);

};

class Remote

{

  friend class Tv;

public:

  void Bool volup(Tv & t){t.volup();};

};

inline void Tv::buzz(Remote & r)

{

  ......

};

由於 Remote 的聲明位於Tv聲明后面,所以可以在類聲明中定義Remote::volup(),但Tv::buzz()方法必須在Tv聲明的外部定義,使

其位於Remote聲明的后面。如果不希望buzz()是內聯的,則應在一個單獨的方法定義文件中定義它。

 

 

共同的友元:

一個成員函數,它可以是一個類的成員,同時是另一個類的友元,但有時將函數作為兩個類的友元更合理。例如,假定有一具Probe類和一個

Analyzer類,前者表示某種可編程的測量設備,后者表示某種可編程的分析設備。這兩個類都有內部時鍾,且希望它們能夠同步,則該包含下

述代碼行:

class Analyzer;//前向聲明

前向聲明使編譯器看到Probe類聲明中的友元聲明時,知道Analyzer是一種類型。

 

class Probe

{

  friend void sync(Analyzer & a ,const Probe & p);

  friend void sync(Probe & p,const Analyzer & a);

};

 

class Analyzer

{

  friend void sync(Analyzer & a ,const Probe & p);

  friend void sync(Probe & p,const Analyzer & a);

};

 

//定義這些友元函數


免責聲明!

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



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