1. 程式人生 > >友元例項:友元類及友元函式

友元例項:友元類及友元函式

        學習了c++這麼久,一直沒有對友元進行了解,據說友元不是特別好用(據說,不是我說的),因此直到今天才去了解。其實友元確實不是很常用,但友元功能確實很實用,它不但能夠釋放類中的非公有成員,同時還能保證了類的封裝性。使用者可以有選擇為具體的類或函式賦予“通行證”。還是比較靈活的。比如:某個類去訪問另一個類的私有成成員,或者一個函式去訪問某個類的私有成員等等,都可以使用友元來實現。

       下面就友元做了兩個小例子,望高手指教。(每段程式碼都在不同的檔案中)

首先是關於友元類的程式碼,就一句話,很簡單。。。

Test.h:

#ifndef TEST_H
#define TEST_H

#include<iostream>
using namespace std;

class Test
{
    friend class FriendTest;    //此處宣告FriendTest為Test的友元類,FriendTest類可以訪問Test的私有成員
    public:
        Test();
        void set(int h,int w);
        void print();
        virtual ~Test();
    protected:
    private:
        int height;
        int weight;
};

#endif // TEST_H

Test.cpp

#include "../include/Test.h"

Test::Test()
{
    //ctor
    height = 0;
    weight = 0;
}
void Test::set(int h, int w)
{
    height = h;
    weight = w;
}
void Test::print()
{
    cout << height << "  ";
    cout << weight <<endl;
}

Test::~Test()
{
    //dtor
}

下面關於FriendTest的相關程式。

FriendTest.h

#ifndef FRIENDTEST_H
#define FRIENDTEST_H

#include "Test.h"

class FriendTest
{
    public:
        FriendTest();
        void setTest(Test& t, int h, int w);    
        virtual ~FriendTest();
    protected:
    private:
};

#endif // FRIENDTEST_H

FriendTest.cpp

#include "../include/FriendTest.h"

FriendTest::FriendTest()
{
    //ctor
}
void FriendTest::setTest(Test& t, int h, int w)     //之前聲明瞭友元,所以此處可以呼叫私有成員
{
   t.height = h;
   t.weight = w;
}
FriendTest::~FriendTest()
{
    //dtor
}

#include <iostream>
#include "./include/Test.h"
#include "./include/FriendTest.h"

using namespace std;

int main()
{
    Test t;
    FriendTest ft;
    t.set(30, 20);
    ft.setTest(t,9,8);
    t.print();
    return 0;
}

接下來是關於友元函式的問題,友元函式我弄了很久,對於某個類來說,只希望其某個函式為友元,需要對函式進行友元宣告。然而將上邊程式碼中的友元類的宣告改成友元函式的宣告,編譯不通過,提示未定義。後來發現對於友元函式來說兩個類必須放在同一個檔案中,並且要有部分調整,具體實現如下,並富有詳解。

部分程式碼省略。。。主要程式碼如下:

#include <iostream>
using namespace std;
//由於兩個類都使用到了另一個類,所以順序很關鍵。如果將兩個類的順序顛倒會出現編譯不通過,並提示未定義。另外友元函式必須在最後實現,因為它用到了兩個類中的成員。仔細與上一部分的程式碼比較,你便會了解裡邊的玄機。。。
class Test;      //首先需要宣告Test類,FriendTest類中需要使用。
class FriendTest
{
    public:
        FriendTest();
        void setTest(Test& t, int h, int w);
        virtual ~FriendTest();
    protected:
    private:
};
class Test
{
    friend void FriendTest::setTest(Test& t, int h, int w);     //友元函式宣告
    public:
        Test();
        void set(int h,int w);
        void print();
        virtual ~Test();
    protected:
    private:
        int height;
        int weight;
};

void FriendTest::setTest(Test& t, int h, int w)
{
   t.height = h;
   t.weight = w;
}

int main()
{
    cout << "friend" <<endl;
    Test t;
    FriendTest ft;
    t.set(30, 20);
    ft.setTest(t,9,8);
    t.print();
    return 0;
}



 另外在網上看到了一個關於primer c++中一個友元例子的講解可能對你理解有些幫助:

          做了部分修改。。。。。。

第一種寫法問題:

編譯到Screen時,由於Screen類使用到Window_Mgr的成員函式,前面給出了Window_Mgr的宣告,但不清楚Window_Mgr的完整定義,對成員函式不清楚,所以友元函式宣告不成立,編譯出錯。

class Window_Mgr

class Screen

{

  public:

    friend Window_Mgr& Window_Mgr::relocate(Window_Mgr::index r, Window_Mgr::index c, Screen& s);       

  private:

    int height;

    int width;

}

class Window_Mgr

{

public:

  typedef std::string::size_type index;

  Window_Mgr& Window_Mgr::relocate(index r, index c, Screen& s)

  {

    s.height += r;

    s.width += c;

    return *this;

  }

}

第二種寫法問題在於:

編譯到relocate時,由於Screen& s的實現使用到Screen的成員變數,雖然前面給出了Screen的宣告,但此時還不清楚Screen的完整定義,所以編譯出錯。

class Screen;

class Window_Mgr

{

public:

  typedef std::string::size_type index;

  Window_Mgr& Window_Mgr::relocate(index r, index c, Screen& s)

  {

    s.height += r;

    s.width += c;

    return *this;

  }

}

class Screen

{

  public:

    friend Window_Mgr& Window_Mgr::relocate(Window_Mgr::index r, Window_Mgr::index c, Screen& s);

  private:

    int height;

    int width;

}

第三種寫法:

將Window_Mgr::relocate的實現移動到最後,由於編譯類Window_Mgr時,並不需要Screen&s 的實現細節,問題得到解決

class Screen;

class Window_Mgr

{

public:

  typedef std::string::size_type index;

  Window_Mgr& Window_Mgr::relocate(index r, index c, Screen& s);   //無內部成員的使用

}

class Screen

{

  public:

    friend Window_Mgr& Window_Mgr::relocate(Window_Mgr::index r, Window_Mgr::index c, Screen& s);

  private:

    int height;

    int width;

}

  Window_Mgr& Window_Mgr::relocate(Window_Mgr::index r, Window_Mgr::index c, Screen& s)

  {

    s.height += r;

    s.width += c;

    return *this;

  }

可見,這兩個類如果編譯成功需要嚴格的交替順序

這也就解釋了為什麼放在兩個檔案中無法編譯。

附錄:

一開始的實現的不能編譯的兩個檔案

實現分別如下:Window_Mgr.h

#ifndef WINDOW_MGR //為了避免兩個檔案巢狀

#define WINDOW_MGR

#include <string>

#include <Screen.h>

class Window_Mgr

{

public:

  typedef std::string::size_type index;

  Window_Mgr& Window_Mgr::relocate(index r, index c, Screen& s)

  {

    s.height += r;

    s.width += c;

    return *this;

  }

}

#endif

Screen.h

#ifndef SCREEN

#define SCREEN

#include "Window_Mgr.h"

class Screen

{

  public:

    friend Window_Mgr& Window_Mgr::relocate(Window_Mgr::index r, Window_Mgr::index c, Screen& s);

  private:

    int height;

    int width;

}

#endif