C++之路 Day8

 

本文是 友元 專題

      什麼是友元?英文翻譯就是Friends。我們在設計一個類的時候考慮到封裝性,訪問權限,會把一些成員變量的訪問屬性定義爲private,但是這樣定義後,如果後期其它類想要訪問這個類中的private訪問屬性的成員變量時,就需要修改那個類對應成員變量的訪問屬性,這樣就修改了代碼。那麼如何做到不修改類成員變量訪問屬性的情況下,可以使其它類或全局函數訪問到這個類的private訪問屬性的成員變量呢?友元,就這麼被設計出來了。當然友元還有一種用途:要對流輸出符<<重載時,這個後面再講解。

      先說說哪些我們已知的東西可以作爲友元?全局函數,成員函數,一個類(中的全部成員函數)。直接看例子吧:

//全局函數作友元
#include <iostream>
#include <cmath>

using namespace std;

class Point{
public:
    Point(double x, double y){
        _x = x;
        _y = y;
    }
    void getFormatxy();
    friend double distance(Point &a, Point &b);
private:
    double _x, _y;
};

void Point::getFormatxy(){
    cout<<"("<<_x<<","<<_y<<")"<<endl;
}
double distance(Point &a, Point &b){
    double dx = a._x - b._x;
    double dy = a._y - b._y;
    return sqrt(dx*dx + dy*dy);
}

int main()
{
    Point p1(3.0, 4.0), p2(6.0, 8.0);
    p1.getFormatxy();
    p2.getFormatxy();
    double d = distance(p1, p2);
    cout<<"distance is "<<d<<endl;
    return 0;
}
//一個類的成員函數作友元
#include <iostream>
#include <cmath>

using namespace std;

class Point;    //前向聲明,用於聲明。

class ManagerPoint{
public:
    double distance(Point &a, Point &b);
};

class Point{
public:
    Point(double x, double y){
        _x = x;
        _y = y;
    }
    void getFormatxy();
    friend double ManagerPoint::distance(Point &a, Point &b);
private:
    double _x, _y;
};

void Point::getFormatxy(){
    cout<<"("<<_x<<","<<_y<<")"<<endl;
}

double ManagerPoint::distance(Point &a, Point &b){
    double dx = a._x - b._x;
    double dy = a._y - b._y;
    return sqrt(dx*dx +dy*dy);
}

int main()
{
    Point p1(3.0, 4.0), p2(6.0, 8.0);
    p1.getFormatxy();
    p2.getFormatxy();
    ManagerPoint mp;
    float d = mp.distance(p1, p2);
    cout<<"distance is"<<d<<endl;
    return 0;
}

 

//友元類
#include <iostream>
#include <cmath>

using namespace std;

class Point{
public:
    friend class ManagerPoint;
    Point(double x, double y){
        _x = x;
        _y = y;
    }
    void getFormatxy();
private:
    double _x, _y;
};

void Point::getFormatxy(){
    cout<<"("<<_x<<","<<_y<<")"<<endl;
}

class ManagerPoint{
public:
    double distance(Point &a, Point &b);
};

double ManagerPoint::distance(Point &a, Point &b){
    double dx = a._x - b._x;
    double dy = a._y - b._y;
    return sqrt(dx*dx + dy*dy);
}

int main()
{
    Point p1(3.0, 4.0), p2(6.0, 8.0);
    p1.getFormatxy();
    p2.getFormatxy();
    ManagerPoint mp;
    float d = mp.distance(p1, p2);
    cout<<"distance is"<< d<<endl;
    return 0;
}

 友元小結:

      在需要允許某些特定的非成員函數訪問一個類的私有成員(及受保護成員),而且同時仍阻止一般的訪問的情況下,友元是可用的。

      優點:可以靈活地實現需要訪問若干類的私有或受保護的成員才能完成的任務。便於與其它不支持類概念的語言(如C語言、彙編)進行混合編程。通過使用友元函數重載可以更自然地使用C++語言的IO流庫。通過友元直接訪問私有成員也是可以提高程序的運行效率的。

      缺點:一個類對其非公有成員的訪問權限授予其它函數或者類,會破壞類的封裝性,降低該類的可靠性和可維護性。

      注意:友元關係不能被繼承,友元關係不具有傳遞性。

      應將所有的友元關係聲明放在類定義體的開始部分,並且在其前面不應帶有任何訪問權限限定符。

      友元關係是授予的,而不是獲得的。比如爲了使 類B 成爲 類A 的友元,類A必須顯式地將類B聲明爲它的友元。

      將重載函數指定爲類的友元也是可行的,想成爲友元的每個重載函數,都必須在類定義中顯式地聲明成這個類的友元。

 

聲明爲誰的友元,就可以通過誰的對象訪問誰的私有數據成員

補充:流輸出符的重載

一個問題:爲什麼C++流輸入輸出必須重載爲友元函數?

      因爲 cout 類的 operator << 函數的源碼無法改變,所以無法重載爲類的成員函數,操作符重載過程中,需要用到類的成員變量,所以只是重載全局函數,無法使用類的成員變量,所以將操作符重載爲友元函數,如果是重載雙目操作符(即爲類的成員函數),就只要設置一個參數作爲右側運算量,而左側運算量就是對象本身。而 >>或<< 左側運算量是 cin或cout 而不是對象本身,所以不滿足後面一點。就只能申明爲友元函數了。(其實這個地方本人還沒完全理解,歡迎博友留言做進一步的解釋!)

      用法舉例:

#include <iostream>

using namespace std;

class Complex{
    friend ostream & operator<<(ostream &os, const Complex &c){
        os<<"("<<c._x<<","<<c._y<<")"<<endl;
    }
public:
    Complex(float x, float y):_x(x),_y(y){}
private:
    float _x;
    float _y;
};

int main()
{

    Complex c(1, 2), c2(2, 3);
    cout<<c<<c2;    //等價於下面代碼
    operator<<(operator<<(cout,c), c2);
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章