1. 程式人生 > >C++筆記(十八)——友元

C++筆記(十八)——友元

一、友元的產生條件:

  • 類的主要特點之一是資料隱藏,即類的私有成員只能在類定義的範圍內使用,也就是說私有成員只能通過它的成員函式來訪問
  • 但是,有時候需要在類的外部訪問類的私有成員。為此,就需要尋找一種途徑,在不放棄私有資料安全性的情況下,使得類外部的函式或類能夠訪問類中的私有成員——友元

注:

  • 友元相當於一扇通往私有成員的後門
  • 友元是外部的函式,它可分為:友元函式,友元成員,友元類

二、說明

友元的缺點: 破壞了函式的封裝性

2.1 普通函式作為友元

      友元函式不是當前類的成員函式,而是獨立於當前類的外部函式

,但它可以訪問該類的所以物件的成員,包括私有成員和公有成員。

        在類定義宣告友元函式時,需在其函式名前上關鍵字friend函式名後的括號內要加類名及類的物件。此宣告可以放在公有部分,也可以放在私有部分。友元函式可以定義在類的內部,也可以定義在類的外部。

#include <iostream>
#include <string>

using namespace std;

class CString
{
	char *str;
public:
	CString(char *str)
	{
		cout << "構造CString " << endl;
		this->str = new char[strlen(str) + 1];
		strcpy(this->str, str);
	}
	~CString()
	{
		cout << "析構CString" << endl;
		delete[] str;
	}
	void print()
	{
		cout << "CString: " << str << endl;
	}

	friend void my_print(CString &ob); //定義友元函式,括號中加要連線的類的例項化物件
};

void my_print(CString &ob)
{
	cout << "--------------" << endl;
	cout <<"通過友元my_print訪問私有資料: " << ob.str << endl; //通過友元訪問私有資料
	ob.print();//訪問公有資訊
}

int main()
{
	{
		CString ob1("Chen Xingwei");
		ob1.print();
		my_print(ob1);
	}

	getchar();
	return 0;
}

注:

  • 友元函式雖然可以訪問類物件的私有成員,但它畢竟不是成員函式。因此,在類的外部定義友元函式時,不必像成員函式那樣,在函式名前加上“類名::”
  • 友元函式一般帶有一個該類的入口引數(當前類名 &例項化的物件名)。因為友元函式不是類的成員,所以它不能直接引用物件成員的名稱,也不能通過this指標引用物件的成員,它必須通過作為入口引數傳遞進來的物件名或物件指標來引用該物件的成員。
  • 一個函式需要訪問多個類時友元函式非常有用,普通的成員函式只能訪問其所屬的類,但是多個類的友元函式能夠訪問相應的所有類的資料
  • 友元函式通過直接訪問物件的私有成員,提高了程式執行的效率
    。在某些情況下, 如運算子被過載時,需要用到友元。但是友元函式破壞了資料的隱蔽性,降低了程式的可維護性,這與面向物件的程式設計思想是背道而馳的,因此使用友元函式應謹慎

2.2 類的成員函式作為友元

        除了一般的函式可以作為某個類的友元外,一個類的成員函式也可以作為另一個類的友元,這種成員函式不僅可以訪問自己所在類物件中的私有成員和公有成員,還可以訪問friend宣告語句所在類物件中的私有成員和公有成員,這樣能使兩個類相互合作、協調工作,完成某一任務

#include <iostream>
#include <string>
class CBoy; //向前宣告
using namespace std;

class CGirl
{
private:
	char m_name[64];
	int m_age;
public:
	CGirl(char *m_name, int age)
	{
		strcpy(this->m_name, m_name);
		this->m_age = age;
	}
	void girl_print(CBoy &ob); //友元,訪問CBoy
};

class CBoy
{
private:
	char m_name[64];
	int m_age;
public:
	CBoy(char *m_name, int age)
	{
		strcpy(this->m_name, m_name);
		this->m_age = age;
	}
	friend void CGirl::girl_print(CBoy &ob);
};

void CGirl::girl_print(CBoy &ob) //友元,訪問CBoy
{
	cout << "girl.name = " << m_name << endl;
	cout << "girl.age = " << m_age << endl;
	cout << "boy.name = " << ob.m_name << endl;
	cout << "boy.age = " << ob.m_age << endl;
}

int main()
{
	{
		CBoy Bob((char *)"Bob", 22);
		CGirl Lucy((char*)"Lucy", 15);
		Lucy.girl_print(Bob); //先呼叫girl類,再通過友元調boy類,然而girl在boy下面,所以需要先宣告
	}
	
	getchar();
	return 0;

}

說明:

  • 一個類的成員函式作為另一個類的友元函式時,必須先定義這個類。例如上例中,類CGirl的成員函式為類CBoy的友元函式,必須先定義類CGirl。並且在宣告友元函式時,要加上成員函式所在類的類名,如:
friend void CGirl::girl_print(CBoy &物件名);
  • 程式中還要使用“向前引用”(引用添加了友元函式的那個類,這裡是CBoy),因為函式girl_print()中將“CBoy &物件名”作為引數,而CBoy要晚一些時候才定義

2.3 友元類

    不僅函式可以作為一個類的友元,一個類也可以作為另一個類的友元。這種友元類的宣告方法是在另一個類宣告中加入語句“friend 類名;”,其中的“類名”即為友元類的類名。此語句可以放在公有部分也可以放在私有部分。

class Y
{
  //.....
};

class X
{
  //...
  friend Y;
  //...
};

當一個類被宣告為另一個類的友元時,它的所以的成員函式都成為另一個類的友元函式,這就意味著為友元的類中的所有成員函式都可以訪問另一個類的私有成員。

注:

  • 友元關係是單向的,不具有交換性(我是你的朋友,不能推斷出你是我的朋友)
  • 友元關係也不具有傳遞性(我是你的朋友,你是他的朋友,不能推斷出:我是他的朋友)