友元類


友元類所有的方法都可以訪問原始類的私有成員和保護成員

聲明

friend class ClassName

友元聲明可以位於公有、私有或保護部分,其所在的位置無關緊要。由於Remote類提到了Tv類,

所以編譯器必須了解Tv類后才能處理Remote類,為些最簡單的方法是首先定義Tv類。也可以使用前向聲明,稍后介紹

#include<iostream>

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

class Tv
{
private:
    int mode;
    int volume;
public: 
    friend class Remote;
    Tv(){};
    void Show()
    {
        cout<<"welcome Tv "<<mode<<endl;
    }
    void set_mode(int i){
        mode=i;
        cout<<" mode = "<<mode<<endl;
    };

};

class Remote
{
private:
    int mode;
public:
    void volup(Tv &t,int i)
    {
        t.volume=i;
        cout<<" current volume  "<<t.volume<<endl;
    };
    void ShowTv(Tv &t)
    {
        t.Show();
    }
};


int main()
{
    Tv _t;
    _t.set_mode(3);
    Remote _r;
    _r.volup(_t,2);
    _r.ShowTv(_t);
    cin.get();
}

 上邊的例子中代碼真正訪問Tv成員的是Remote方法是volup,其它的方法不是真需要作為友元的。因此它是唯一需要作為友元的方法

確實可以選擇僅讓特定的類成員成為另一個類的友元,需不必須讓整個類成為友元,但這樣稍微有點麻煩,必須小心排列各種聲明和定義的順序。

讓Remote::volup()成為Tv類的友元的方法,在Tv類聲明中將其聲明為友元

class

{

  friend void Remote::set_chan(Tv & t,int c);

};

 然而,要使編譯器能夠處理這條語句,它必須知道Remote的定義,否則就無法知道Remote是一個類,而set_chan是這個類的方法。

這意味着就將Remote的定義放到Tv的定義前面。Remote的方法提到了Tv對象,而這意味着Tv定義應當位於Remote定義之前。避開這種循環

依賴的方法是,使用前向聲明(forward declaration)為此,需要在Remote定義的前面插入下面的語句:

class Tv

這樣排列次序應如下:

class Tv;

class Remote{...};

class Tv {....};

 還有一個麻煩。程序清單Remote聲明包含了內聯代碼,例如:

void volup(Tv &t,int i)
{
t.volume=i;
cout<<" current volume "<<t.volume<<endl;
};

由於這將調用Tv的一個方法,所以編譯器此時必須已經看到了Tv類的聲明,這樣才能知道Tv有哪些方法,但正如看到的,該聲明位於Remote聲明方法后

。這種問題的解決方法是,使用Remote聲明中只包含方法聲明,並將實際的定義放在Tv類之后。這樣

Remote方法的原型與下面類似:

void volup(Tv &t,int i);

檢查該原型時,所有的編譯器都需要知道Tv是一個類,而前向聲明提供了這樣的信息。當編譯器到達真正的方法定義時,它已經讀取了Tv類的聲明,

並擁有了編譯這些方法所需的信息。能過在方法定義中使用inline關鍵字,仍然可以使其成為內聯方法。

#include<iostream>

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

class Tv;

class Remote
{
private:
    int mode;
public:
    void volup(Tv &t,int i);
    void ShowTv(Tv &t);
};

class Tv
{
private:
    int mode;
    int volume;
public: 
    friend void Remote::volup(Tv & t,int c);
    Tv(){};
    void Show()
    {
        cout<<"welcome Tv "<<mode<<endl;
    }
    void set_mode(int i){
        mode=i;
        cout<<" mode = "<<mode<<endl;
    };

};

inline void Remote::volup(Tv & t,int i)
{
    t.volume=i;
    cout<<" current volume  "<<t.volume<<endl;
};

inline void Remote::ShowTv(Tv & t)
{
    t.Show();
};

int main()
{
    Tv _t;
    _t.set_mode(3);
    Remote _r;
    _r.volup(_t,2);
    _r.ShowTv(_t);
    cin.get();
}

內聯函數的鏈接性是內部的,這意味着函數定義必須在使用函數的文件中,這個例子中內聯定義位於頭文件中,因此在使用函數的文件中包含

頭文件可確保將定義放在正確的地方。這可以將定義放在實現文件中,但必須刪除關鍵字inline這樣函數的鏈接性將是外部的

 

 再看一個操作符重載的小例子

#include<iostream>
   

class B
{
public :
    B()
    {
        myValue=2;
        std::cout<<"B init"<<std::endl;
    }
    ~B()
    {
        std::cout<<"B end"<<std::endl;
    }
    
    //這樣做可以
    /*B operator+(const B av)
    {
        B a;
        a.myValue=myValue+av.myValue;
        return a;
    }*/
    //也可以這么做
    friend B operator+(const B b1,const B b2);

    //------------------------------------------------
    int GetMyValue()
    {
        return myValue;
    }
    //重載<<
    friend std::ostream& operator<<(std::ostream& os,B);
private :
    int myValue;
};

B operator+(const B b1,const B b2)
{
    B a;
    a.myValue=b1.myValue+b2.myValue;
    return a;
}
std::ostream& operator<<(std::ostream& os,B b)
{
    return os<<"重載實現:"<<b.myValue<<std::endl;
}
int main()
{
    B b1;
    std::cout<<b1;
    B b2;
    B b3=b1+b2;
    std::cout<<b3<<std::endl;
    std::cin.get();
    return 0;
}

 

 

 


免責聲明!

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



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