C++基礎教程:友元類與物件導向

來源:互聯網
上載者:User

C++基礎教程:友元類與物件導向

因為後續準備入職的公司都希望能轉C++,所以最近也是按部就班的開始進行C++的學習。然後這個系列的文章打算探究C++的語言特性,也比較一下不同語言(如Java,Scala,Python,Go)之間的設計哲學,同時也拋磚引玉的希望能有大拿們的指點。最近在學習過程之中接觸了友元函數與友元類的概念,第一篇文章我們就聊聊友元的概念。

1.友元函數:

開篇先簡單介紹一下友元這個概念吧。
在C++之中,類的友元函數是定義在類外部,但它有權訪問類的所有私人(private)成員和保護(protected)成員。儘管友元函數的原型有在類的定義中出現過,但是友元函數並不是成員函數。友元可以是一個函數,該函數被稱為友元函數;友元也可以是一個類,該類被稱為友元類。

直接上個代碼,我們看看友元函數與友元類具體是怎麼樣使用的:

#include <iostream>using namespace std;class Box {public:    Box(double l, double b, double h) {        length = l;        breadth = b;        height = h;    }    friend class A;    friend voidboxPrintBox(Box &box);private:    double length;    double breadth;    double height;};//友元函數,友元函數並非成員函數voidboxPrintBox(Box &b) {    cout << b.height << " " << b.length << " " << b.breadth << endl;}//友元類class A {public:    voidprintBox(Box &b) {        cout << b.height << " " << b.length << " " << b.breadth << endl;    }};intmain() {    Box box(1,2,3);        //友元函數,可以訪問Box類的private的變數    boxPrintBox(box);    //友元類,同樣可以訪問Box類的private的變數    A a;    a.printBox(box);            return 0;}

上面的代碼可以看出,友元函數和友元類都可以直接存取到對象的私人變數。接下來我們來分析一下友元函數的特點。

  • 1、為什麼要引入友元函數:
    在實作類別之間資料共用時,減少系統開銷,提高效率。具體來說:為了使其他類的成員函數直接存取該類的私人變數。即:允許外面的類或函數去訪問類的私人變數和保護變數,從而使兩個類共用同一函數。能夠提高效率,表達簡單、清晰
  • 2、什麼時候使用友元函數:
    1)運算子多載的某些場合需要使用友元。
    2)兩個類要共用資料的時候
  • 3、友元方式的缺點:
    1)友元函數破環了封裝機制,除非不得已的情況下才使用友元函數。
2.友元關係與物件導向:

接下來我們聊聊怎麼從物件導向的角度去理解友元關係。(以下內容皆為個人理解,有不準確之處望能斧正)

  • 1)友元函數
     友元函數是不從屬與類的函數,除了能夠訪問類的私人變數之外,與其他實現在類外部的函數無異。從物件導向的角度看,函數是不應該獨立實現於類之外的。顯然獨立與類之外的友元函數,從物件導向的角度來思考,是不優雅的解決方式。
     這和C++本身相容C文法有關,如操作符<<的重載利用的就是友元函數。<<的函數重載如下,這個函數是獨立與類之外實現的方法:

    friend ostream &operator<<(ostream &output, const object &o)

      顯然,這個<<的函數方法更應該從屬在ostream這個類之中,做為一個可重載的方法實現。如下定義

    friend ostream &operator<<(const object &o) //作為ostream的類函數
      如Java,Scala,Python都不支援獨立與類存在定義的函數了。在物件導向的角度,後續的語言實現的更加純粹了。所以如果本身代碼風格趨近與物件導向的風格,就應該盡量理由友元類來實現需要的功能,而不是使用友元函數。
      有朋友私信我說:Python之中明明直接def就可以定義函數了,也不需要類啊,這是不是也不符合物件導向的邏輯思維?這裡簡單解釋一下,Python之中的每個函數,都會被封裝成一個function對象,所以Python之中是一切皆對象的,不會存在獨立與類存在的函數。而如同Java與Scala之中的lambda運算式,也是封裝為匿名類存在的。
  • 2)友元類
    通過友元類封裝之中,友元類搖身一變,類之中所有的方法都成為友元函數了。看起來並不會破壞上文提到的物件導向的邏輯了,但是涉及到繼承又存在一些不大不小的坑,我們一起來捋一捋:
      友元關係不能繼承。基類的友元對衍生類別的成員沒有特殊存取權限。如果基類被授予友元關係,則只有基類具有特殊存取權限,該基類的衍生類別不能訪問授予友元關係的類。
      好複雜,我們直接上代碼:

#include <iostream>using namespace std;class A {private:    int x;friend class C;};class B:public A{private:    int y;};class C {    voidprintA(A& a) {        cout << a.x << endl;    }    voidprintB(B& b) {        cout << b.x << endl; //C類依然可以利用友元關係訪問從B類從A類繼承來的私人變數        //cout << b.y << endl; C類不可以訪問B類的私人變數,友元關���不繼承,該語句不合法。    }};

顯然,類C與A的友元關係止步於繼承處,類C沒法訪問類B新定義的私人變數。(這裡留一個小問題給大家,如果類B覆蓋了類A的私人變數x,類C之中的printB是否仍然可以通過編譯呢?)

我們再看一段不同的代碼:

#include <iostream>using namespace std;class A{    int x;public:    friend class B;};class B{public:    voidfun(A& a){ cout << a.x << endl;}};class C:public B{public:    //void fun2(A& a){ cout <<a.a <<endl;}   //衍生類別新加的函數卻不能訪問A,此句會報錯};voidmain(){    A a;    C c;    c.fun(a); //C是B的衍生類別,只能基類B的函數fun來訪問A的對象}

類C雖然繼承了類B,但是也不在具有了和A的友元關係,只能"拼爹"。依賴從類B之中繼承的友元函數來訪問類A。(這裡同樣留一個小問題給大家,如果類B之中的fun函數是protected或private的,那上述代碼還能夠正常編譯嗎?)

  在這裡做一個簡單的小結:友元關係在友元類之中沒有繼承性,只能依賴基類的友元關係。

3.非C++語言是怎麼解決友元關係的:
  • Java
    JAVA修飾符類型(public,protected,private)
  • public的類、類屬變數及方法,包內及包外的任何類均可以訪問;
  • protected的類、類屬變數及方法,包內的任何類,及包外的那些繼承了此類的子類才能訪問;
  • private的類、類屬變數及方法,包內包外的任何類均不能訪問;
  • 如果一個類、類屬變數及方法不以這三種修飾符來修飾。那麼包內的任何類都可以訪問它,而包外的任何類都不能訪問它(包括包外繼承了此類的子類)。所以這種類型有時也稱為friendly類型,現在知道這個名字的出處了吧,大家對同一個package之中要放哪些類有木有新的認識了呢?

  • Scala
    在Scala之中,private和protected可以指定額外的參數。可以使用private[AccessQualifier],AccessQualifier可以是this,也可以是其它的類名或包名。這樣就可以這麼理解:這個成員對所有類都是private,除了自己和AccessQualifier所表示範圍內的類。這個概念也是可以遞推的,也就是說,如果AccessQualifier是一個類,那麼private成員對於AccessQualifier的AccessQualifier也是可見的。
    (好優雅的方式啊,我愛Scala。)

  • Python
    老子他喵的沒有存取控制,全靠自覺。
  • Golang
    比較粗暴,就靠首字母的大小寫區分。沒法做到細粒度的控制,不過看起來也不影響絕大多數情境的工程實現。所以是否這樣化繁為簡的設計哲學,也是一種優雅的設計呢?

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.