一篇文章詳細解釋C++的友元(friend)

一.友元函數

友元函數可以是普通函數或者類成員函數。

先看普通函數聲明為友元函數:

如下所示:

#include <iostream>
#include <cmath>
using namespace std;
class Point
{
    //普通函數聲明為類的友元函數
	friend double TwoPointsDistant(const Point& pnt1, const Point& pnt2);
public:
	Point(double x=0, double y=0)
		:_x(x), _y(y)
	{}
	double getPointXAxis() const { return this->_x; }
	double getPointYAxis() const { return this->_y; }
private:
	double _x;
	double _y;
};
//計算兩點的距離
double TwoPointsDistant(const Point& pnt1, const Point& pnt2)
{
	return sqrt(  pow((pnt1._x - pnt2._x), 2) + pow((pnt1._y - pnt2._y), 2)   );
}
int main()
{
	Point point1(1.1,2.2);
	Point point2(3.3, 4.4);
	cout << TwoPointsDistant(point1, point2) << endl;
	system("pause");
	return 0;
}

這裡說明一點:TwoPointsDistant()函數必須在Point類的定義下面,至於原因,很簡單,你若放在Point上面,Point的數據成員_x和_y都沒定義呢,你用個錘子。

再看類成員函數聲明為友元函數:

還以上面的類為例,現在加一個_PointMove_類,它有一個成員函數_PointAxisAddOne,_作用是將點的坐標都加1。如下:

class PointMove
{
public:
	void PointAxisAddOne(Point& pnt);
};

這裡就出現瞭一個問題:_Point_和_PointMove_哪個放在前面?先給出答案,應該把_PointMove_放在前面,並且是必須的,如下:

class Point;//前向聲明Point
class PointMove
{
public:
	void PointAxisAddOne(Point& pnt);
};
class Point
{
    //普通函數聲明為類的友元函數
	friend double TwoPointsDistant(const Point& pnt1, const Point& pnt2);
    //類成員函數聲明為友元
	friend void PointMove::PointAxisAddOne(Point& pnt);
    /*這裡同前*/
};
//類成員函數的定義
void PointMove::PointAxisAddOne(Point& pnt)
{
	pnt._x = pnt._x + 1;
	pnt._y = pnt._y + 1;
}

這裡註意,對於類的成員函數,聲明為其他類的友元函數時需要加上類的作用域,即指出該函數屬於哪個類。如上面的_PointMove::_。​

同時,需要說明的是,PointAxisAddOne()函數的定義是必須放在Point類定義後面的,這和普通函數的道理是一樣的。

最後說明

1.一個函數Func被聲明為類A的友元函數,那麼是不能直接使用this指針來訪問類A的數據成員的(當然,若Func是類B的成員函數,它可以通過this訪問類B的數據成員),這和成員函數不同。

2.一個函數Func為什麼要聲明為某個類A的友元,就是因為函數的參數類型為類A類型,我想訪問這個類對象的數據成員,所以被聲明為類A的友元函數的參數類型必定為類A,如friend Func(A& obj);

二.友元類

若是將一個類C都聲明為另一個類A的友元類,則類C中的成員函數均可訪問類A中的私有數據成員。如下:

class Point
{
    //友元類
    friend class PointInfo;
    ...
}
class PointInfo
{
public:
	//打印點所處象限
	void PrintQuadrant(const Point& pnt) const
	{
		if (pnt._x > 0 && pnt._y > 0)
			cout << "點"<<"(" << pnt._x << "," << pnt._y<<")" <<"處於第一象限" << endl;
	}
};

當然,你也可以把_PointInfo_寫在_Point_前,隻是函數_PrintQuadrant()_的定義就不能在類內實現瞭,隻能在_Point_後實現,原因和前面一樣,不再贅述。

三.完整示例:

#include <iostream>
#include <cmath>
using namespace std;
class Point;
class PointMove
{
public:
	void PointAxisAddOne(Point& pnt);
};
class PointInfo
{
public:
	//打印點所處象限
	void PrintQuadrant(const Point& pnt) const;
};
class Point
{
	friend class PointInfo;
	friend double TwoPointsDistant(const Point& pnt1, const Point& pnt2);
	friend void PointMove::PointAxisAddOne(Point& pnt);
public:
	Point(double x=0, double y=0)
		:_x(x), _y(y)
	{}
	double getPointXAxis() const { return this->_x; }
	double getPointYAxis() const { return this->_y; }
	void PrintAxis(const Point& pnt) const
	{
	}
private:
	double _x;
	double _y;
};
//打印點所處象限
void PointInfo::PrintQuadrant(const Point& pnt) const
{
	if (pnt._x > 0 && pnt._y > 0)
	cout << "點"<<"(" << pnt._x << "," << pnt._y<<")" <<"處於第一象限" << endl;
}
void PointMove::PointAxisAddOne(Point& pnt)
{
	pnt._x = pnt._x + 1;
	pnt._y = pnt._y + 1;
}
double TwoPointsDistant(const Point& pnt1, const Point& pnt2)
{
	return sqrt(  pow((pnt1._x - pnt2._x), 2) + pow((pnt1._y - pnt2._y), 2)   );
}
int main()
{
	Point point1(1.1,2.2);
	Point point2(3.3, 4.4);
	cout << TwoPointsDistant(point1, point2) << endl;
	PointInfo pf;
	pf.PrintQuadrant(point1);
	system("pause");
	return 0;
}

VS2015打印結果:

打印結果

四.同一個類(class)的類對象(object)互為友元

還以上面給出的例子為基礎,現在在_Point_類加一個成員函數func(const Point& pnt),它返回點的x軸和y軸的和。如下所示。

class Point
{
    /*這裡同上*/
	double func(const Point& pnt)
	{
		return pnt._x + pnt._y;
	}
private:
	double _x;
	double _y;
};

現在我生成兩個對象,並作如下操作:

	Point point1(1.1,2.2);
	Point point2(3.3, 4.4);
	cout << point1.func(point2) << endl;

最後的結果是打印出7.7。看到這裡不知道你有沒有疑問:為什麼可以通過point1直接訪問point2的私有數據成員,而沒有將func()聲明為友元函數?侯捷老師是這麼解釋的:相同class的各個objects之間互為友元。

所以對於一個類A,若有一個成員函數Fun(A& arg),可以通過arg直接訪問A的私有數據成員。

總結

本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!

推薦閱讀: