友元是C++提供的一種破壞數據隱蔽和封裝的機制
1.友元函數
友元函數是在類中使用關鍵字friend修飾的非成員函數
1.1友元普通函數
定義與概念
- 友元函數是一個普通的函數
- 友元普通函數在實現時,不需要類名的限定;在調用時,也不需要由實例來調用
示例代碼
#include <iostream>
#include <cmath>
using namespace std;
class Point{
public:
Point(int x = 0,int y = 0):x(x),y(y){}
int getX(){ return x;}//內聯函數
int getY(){ return y;}
void showData();
friend float dist(Point &p1,Point &p2);//聲明友元函數
private:
int x,y;
};
//普通成員函數的實現,需要類名限定
void Point::showData(){
cout << "x: " << x << ", y: " << y << endl;
}
//因為友元函數是非成員函數,所以不需要類名限制
float dist(Point &p1,Point &p2){
double x = p1.x - p2.x;
double y = p1.y - p2.y;
return static_cast<float>(sqrt(x*x + y*y));
}
int main()
{
Point p1(1,1),p2(4,5);
p1.showData();
p2.showData();
//調用友元普通函數時,也不需要由類的實例來調用
cout << "the distance is : " << dist(p1,p2) << endl;
return 0;
}
1.2友元成員函數
定義與概念
- 友元函數是其它類的成員函數
- 必須先定義包含成員函數的類(比如說A),再在另外一個類(比如說B)中將該成員函數聲明為友元函數。此時雖然這個友元函數是A的成員函數,該友元函數仍然稱為非成員函數(對於B來說)
示例代碼
#include <iostream>
#include <cmath>
using namespace std;
//前向引用聲明,否則報錯 error: 'Point' does not name a type
class Point;
class Line{
public:
//這時不能使用這樣的形式進行初始化:Line(Point p1,Point p2):p1(p1),p2(p2){}
//因為此時Point的結構尚未定義,error: field 'p1' has incomplete type
Line(Point p1,Point p2);
Point& getP1(); //把引用當做函數返回值
Point& getP2();
float dist();
private:
//不可以這樣定義成員變量:Point p1,p2;因為此時Point結構尚不完善
Point &rp1,&rp2;//類(引用)的組合
};
class Point{
public:
Point(int x = 0,int y = 0):x(x),y(y){}
int getX(){ return x;}//內聯函數
int getY(){ return y;}
void showData();
//聲明友元成員函數
friend float Line::dist();
private:
int x,y;
};
void Point::showData(){
cout << "x: " << x << ", y: " << y << endl;
}
//Line類函數的延遲實現開始
//當一個類的成員變量是引用時,需要在初始化列表中初始化引用
//否則報錯:error: uninitialized reference member
Line::Line(Point p1,Point p2):rp1(p1),rp2(p2){}
Point& Line::getP1(){
return rp1;
}
Point& Line::getP2(){
return rp2;
}
float Line::dist(){
double x = rp1.x - rp2.x;
double y = rp1.y - rp2.y;
return static_cast<float>(sqrt(x*x + y*y));
}
//Line類函數的延遲實現結束
int main()
{
Point p1(1,1),p2(4,5);
p1.showData();
p2.showData();
Line line(p1,p2);
cout << "the distance is : " << line.dist() << endl;
return 0;
}
2.友元類
定義與概念
示例代碼
#include <iostream>
#include <cmath>
using namespace std;
class Point{
public:
Point(int x = 0,int y = 0):x(x),y(y){}
int getX(){ return x;}//內聯函數
int getY(){ return y;}
void showData();
//聲明友元類,否則編譯不通過,error: 'int Point::x' is private
friend class Line;
private:
int x,y;
};
void Point::showData(){
cout << "x: " << x << ", y: " << y << endl;
}
class Line{
public:
Line(Point p1,Point p2):p1(p1),p2(p2){}
Point getP1(){ return p1;}
Point getP2(){ return p2;}
float dist();
private:
Point p1,p2;//類的組合
};
float Line::dist(){
double x = p1.x - p2.x;
double y = p1.y - p2.y;
return static_cast<float>(sqrt(x*x + y*y));
}
int main()
{
Point p1(1,1),p2(4,5);
p1.showData();
p2.showData();
Line line(p1,p2);
cout << "the distance is : " << line.dist() << endl;
return 0;
}
3.友元的性質
- 友元關系是不能傳遞的,如A是B的友元,B是C的友元,但A不是C的友元
- 友元關系是單向的,A是B的友元,A可以訪問B的私有屬性,反之不成立
- 友元關系是不被繼承的,A是B的友元,但A的派生類不是B的友元
4.總結
- 友元是一種破壞數據隱蔽和封裝的機制,這是它的用處也是它的壞處。應該盡量避免使用友元
- 示例代碼
#include <iostream>
#include <cmath>
using namespace std;
class Point{
public:
Point(int x = 0,int y = 0):x(x),y(y){}
int getX(){ return x;}//內聯函數
int getY(){ return y;}
void showData();
float dist(Point p);
private:
int x,y;
};
void Point::showData(){
cout << "x: " << x << ", y: " << y << endl;
}
//通過這樣的方式,可以避免使用友元
float Point::dist(Point p){
double x = p.x - this->x;
double y = p.y - this->y;
return static_cast<float>(sqrt(x*x + y*y));
}
int main()
{
Point p1(1,1),p2(4,5);
p1.showData();
p2.showData();
cout << "the distance is : " << p1.dist(p2) << endl;
return 0;
}
5.補充
5.1前向聲明
- 在C++里面可以聲明一個類而不定義它。這個聲明被稱為前向聲明(forward declaration)。
- 在聲明之后,定義之前,這個類是一個不完全類型(incompete type),即已知它是一個類型,但不知道包含哪些成員,具有哪些操作。
- 不完全類型只能以有限方式使用,不能定義該類型的對象,不完全類型只能用於定義指向該類型的指針及引用,或者用於聲明(而不是定義)使用該類型作為形參類型或返回類型的函數。