C++友元函数的分析和理解

参考链接
[url]http://blog.csdn.net/insistgogo/article/details/6608672[/url]
[url]http://www.cnblogs.com/fzhe/archive/2013/01/05/2846808.html[/url]

[b]1、为什么要引入友元函数:在实现类之间数据共享时,减少系统开销,提高效率[/b]
具体来说:为了使其他类的成员函数直接访问该类的私有变量
即:允许外面的类或函数去访问类的私有变量和保护变量,从而使两个类共享同一函数
优点:能够提高效率,表达简单、清晰
缺点:友元函数破环了封装机制,尽量不使用成员函数,除非不得已的情况下才使用友元函数。

[b]2、什么时候使用友元函数:[/b]
1)运算符重载的某些场合需要使用友元。
2)两个类要共享数据的时候

[b]3、怎么使用友元函数[/b]
[color=darkred]友元函数的参数[/color]
因为友元函数没有this指针,则参数要有三种情况:
1、 要访问非static成员时,需要对象做参数;--常用(友元函数常含有参数)
2、 要访问static成员或全局变量时,则不需要对象做参数
3、 如果做参数的对象是全局对象,则不需要对象做参数

[color=darkred]友元函数的位置[/color]
因为友元函数是类外的函数,所以它的声明可以放在类的私有段或公有段且没有区别。

[color=darkred]友元函数的调用[/color]
可以直接调用友元函数,不需要通过对象或指针


[color=darkred]友元函数的分类[/color]
根据这个函数的来源不同,可以分为三种方法:
[size=medium]1、普通函数友元函数:
a) 目的:使普通函数能够访问类的友元
b) 语法:声明位置:公有私有均可,常写为公有
声明: friend + 普通函数声明
实现位置:可以在类外或类中
实现代码:与普通函数相同(不加不用friend和类::)
调用:类似普通函数,直接调用[/size]

[size=medium]2、类Y的所有成员函数都为类X友元函数—友元类
a)目的:使用单个声明使Y类的所有函数成为类X的友元
它提供一种类之间合作的一种方式,使类Y的对象可以具有类X和类Y的功能
具体来说:
前提:A是B的友元(=》A中成员函数可以访问B中有所有成员,包括私有成员和公有成员--老忘)
则:在A中,借助类B,可以直接使用~B . 私有变量~的形式访问私有变量

b)语法:声明位置:公有私有均可,常写为私有(把类看成一个变量)
声明: friend + 类名---不是对象啊
调用:[/size]

[size=medium]3、类Y的一个成员函数为类X的友元函数
a)目的:使类Y的一个成员函数成为类X的友元
具体而言:而在类Y的这个成员函数中,借助参数X,可以直接以X。私有变量的形式访问私有变量
b)语法:声明位置:声明在公有中 (本身为函数)
声明:friend + 成员函数的声明
调用:先定义Y的对象y---使用y调用自己的成员函数---自己的成员函数中使用了友元机制[/size]

4、在模板类中使用友元operator<<(对<<运算符的重载)
a)使用方法:
在模板类中声明:
friend ostream& operator<< <>(ostream& cout,const MGraph<VexType,ArcType>& G);

在模板类中定义:
template<class VexType,class ArcType>  
ostream& operator<<(ostream& cout,const MGraph<VexType,ArcType>& G)
{
//函数定义
}

b)注意:
把函数声明非模板函数:
friend ostream& operator<< (ostream& cout,const MGraph& G);

把函数声明为模板函数:
friend ostream& operator<< <>(ostream& cout,const MGraph<VexType,ArcType>& G);  
或:
friend ostream& operator<< <VexType,ArcType>(ostream& cout,const MGraph<VexType,ArcType>& G);

说明:
在函数声明中加入operator<< <>:是将operator<<函数定义为函数模板,将函数模板申明为类模板的友员时,是一对一绑定的
实际的声明函数:这里模板参数可以省略,但是尖括号不可以省略


[color=red][size=medium][b]使用友元类时注意:
(1) 友元关系不能被继承。
(2) 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。
(3) 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明

友元函数和类的成员函数的区别:成员函数有this指针,而友元函数没有this指针。

记忆:A是B的友元《=》A是B的朋友《=》借助B的对象,在A中可以直接 通过B。成员变量(可以是公有,也可以为私有变量) 的方式访问B
[/b][/size][/color]



//测试一:普通函数友元函数
//
class IntegerNumber
{
//
public:
void SetNumber(int a, int b, int c)
{
m_nPublicNumber = a;
m_nProtectedNumber = b;
m_nPrivateNumber= c;
}

//
public:
void PrintPublicNumber()
{
cout<<"[PrintPublicMethod] Public Number is " << m_nPublicNumber << endl;
}

protected:
void PrintProtectedNumber()
{
cout<<"[PrintProtectedMethod] Protected Number is " << m_nProtectedNumber << endl;
}

private:
void PrintPrivateNumber()
{
cout<<"[PrintPrivateMethod] Private Number is " << m_nPrivateNumber << endl;
}

//
//由于友元函数没有this指针,所以一般要求用户传入友元函数所在类的一个对象
//结论一:声明友元函数,友元函数的访问权限不受 public | protected | private 影响
public:
friend void PublicFriend(IntegerNumber& number)
{
cout << "--------------------------------------------------------\n";
//结论二:友元函数可以访问对象的所有成员
cout << "[PublicFriend] Public Number is " << number.m_nPublicNumber << endl; //友元函数可以访问公有成员
cout << "[PublicFriend] Protecdted Number is " << number.m_nProtectedNumber << endl; //友元函数可以访问保护成员
cout << "[PublicFriend] Private Number is " << number.m_nPrivateNumber << endl; //友元函数可以访问私有成员

结论三:友元函数可以访问对象的所有方法
number.PrintPublicNumber(); //友元函数可以访问公有方法
number.PrintProtectedNumber(); //友元函数可以访问保护方法
number.PrintPrivateNumber(); //友元函数可以访问私有方法
}

protected:
friend void ProtectedFriend(IntegerNumber& number)
{
cout << "--------------------------------------------------------\n";
cout << "[ProtectedFriend] Public Number is " << number.m_nPublicNumber << endl; //友元函数可以访问公有成员
cout << "[ProtectedFriend] Protecdted Number is " << number.m_nProtectedNumber << endl; //友元函数可以访问保护成员
cout << "[ProtectedFriend] Private Number is " << number.m_nPrivateNumber << endl; //友元函数可以访问私有成员

number.PrintPublicNumber(); //友元函数可以访问公有方法
number.PrintProtectedNumber(); //友元函数可以访问保护方法
number.PrintPrivateNumber(); //友元函数可以访问私有方法
}

private:
friend void PrivateFriend(IntegerNumber& number)
{
cout << "--------------------------------------------------------\n";
cout << "[PrivateFriend] Public Number is " << number.m_nPublicNumber << endl; //友元函数可以访问公有成员
cout << "[PrivateFriend] Protecdted Number is " << number.m_nProtectedNumber << endl; //友元函数可以访问保护成员
cout << "[PrivateFriend] Private Number is " << number.m_nPrivateNumber << endl; //友元函数可以访问私有成员

number.PrintPublicNumber(); //友元函数可以访问公有方法
number.PrintProtectedNumber(); //友元函数可以访问保护方法
number.PrintPrivateNumber(); //友元函数可以访问私有方法
}

//
public:
int m_nPublicNumber;

protected:
int m_nProtectedNumber;

private:
int m_nPrivateNumber;
};

//
void FriendMethodTest()
{
IntegerNumber cNumber;
cNumber.SetNumber(8, 9, 10);

//友元函数只能采取以下直接调用的方式
//
PublicFriend(cNumber);//直接调用
ProtectedFriend(cNumber);//直接调用
PrivateFriend(cNumber);//直接调用


// C2039: 'PublicFriend' : is not a member of 'IntegerNumber'
//cNumber.PublicFriend(cNumber); //友元函数无法采用一般成员函数的调用方式

//error C2039: 'ProtectedFriend' : is not a member of 'IntegerNumber'
//cNumber.ProtectedFriend(cNumber); //友元函数无法采用一般成员函数的调用方式

//error C2039: 'PrivateFriend' : is not a member of 'IntegerNumber'
//cNumber.PrivateFriend(cNumber); //友元函数无法采用一般成员函数的调用方式
}

void NormalMethodTest()
{
IntegerNumber cNumber;
cNumber.SetNumber(8, 9, 10);

//正常情况下,对象只能访问公有成员,无法访问保护成员和私有成员。
//
cout << "Public Number is " << cNumber.m_nPublicNumber << endl; //一般对象可以访问公有成员

//error C2248: 'IntegerNumber::m_nProtectedNumber' : cannot access protected member declared in class 'IntegerNumber'
//cout << "Protecdted Number is " << cNumber.m_nProtectedNumber << endl; //一般对象无法访问保护成员

//error C2248: 'IntegerNumber::m_nPrivateNumber' : cannot access private member declared in class 'IntegerNumber'
//cout << "Private Number is " << cNumber.m_nPrivateNumber << endl; //一般对象无法访问私有成员


//正常情况下,对象只能访问公有方法,无法访问保护方法和私有方法。
//
cNumber.PrintPublicNumber(); //一般对象可以访问公有方法

//error C2248: 'IntegerNumber::PrintProtectedNumber' : cannot access protected member declared in class 'IntegerNumber'
//cNumber.PrintProtectedNumber(); //一般对象无法访问保护方法

//error C2248: 'IntegerNumber::PrintPrivateNumber' : cannot access private member declared in class 'IntegerNumber'
//cNumber.PrintPrivateNumber(); //一般对象无法访问私有方法
}


void main()
{
NormalMethodTest();
FriendMethodTest();
}


[img]http://chuantu.biz/t5/10/1466043742x3738746553.jpg[/img]



//测试二:指定其他类的方法为自己的友元函数
class Girl;

class Boy
{
public:
Boy(int nId, int nAge)
{
m_nID = nId;
m_nAge = nAge;
}

void Display(Girl& girl);

private:
int m_nID;
int m_nAge;
};

class Girl
{
public:
Girl(int nId, int nAge)
{
m_nID = nId;
m_nAge = nAge;
}

//声明类Boy的成员函数DispGirl()为类Girl的友元函数
friend void Boy::Display(Girl& girl);

private:
int m_nID;
int m_nAge;
};

void Boy::Display(Girl& girl)
{
//访问自己(Boy)的对象成员,直接访问自己的私有变量
cout << "Boy's id is:" << m_nID << ",age:" << m_nAge << endl;

//借助友元,在Boy的成员函数disp中,借助Girl的对象,直接访问Girl的私有变量
//正常情况下,只允许在Girl的成员函数中访问Girl的私有变量
cout << "Girl's id is:" << girl.m_nID<< ",age:" << girl.m_nAge <<endl;
}

void FriendMethodByOtherClassTest()
{
Boy b(123, 18);
Girl g(345, 20);
b.Display(g);
}

void main()
{
FriendMethodByOtherClassTest();
}


[img]http://chuantu.biz/t5/10/1466045254x3738746553.jpg[/img]


//测试三:友元类,可以访问类的全部函数,可以理解成特殊的友元函数
class Wife;
class Husband
{
private:
int m_nMoney;

public:
Husband(int nMoney)
{
m_nMoney = nMoney;
}

void Display(Wife& wife);
};

class Wife
{
private:
int m_nMoney;

friend Husband; //声明类Husband是类Wife的友元类

public:
Wife(int nMoney)
{
m_nMoney = nMoney;
}
};

//函数Display()为类Husband的成员函数,也是类Wife的友元函数
void Husband::Display(Wife& wife)
{
//正常情况,Husband的成员函数disp中直接访问Husband的私有变量
cout << "Husband's money is:" << m_nMoney << endl;

//借助友元类,在Husband的成员函数disp中,借助Wife的对象,直接访问Wife的私有变量
//正常情况下,只允许在Wife的成员函数中访问Wife的私有变量
cout << "Wife's money is:" << wife.m_nMoney <<endl;
}

void FrientClassTest()
{
Husband h(123);
Wife w(456);
h.Display(w); //h调用自己的成员函数,但是以w为参数,友元机制体现在函数Display中
}

void main()
{
FrientClassTest();
}


[img]http://chuantu.biz/t5/10/1466046396x3738746553.jpg[/img]


//测试四:友元函数进行操作符重载
#include <iostream>
#include <string>
using namespace std;

class rect {
int x1, y1, x2, y2; //矩形座标
public:
rect() {
x1 = 0, y1 = 0, x2 = 0, y2 = 0;
}
rect(int m1, int n1, int m2, int n2) {
x1 = m1, y1 = n1, x2 = m2, y2 = n2;
}
void print() {
cout << " x1=" << x1;
cout << " y1=" << y1;
cout << " x2=" << x2;
cout << " y2=" << y2;
cout << endl;
}
//rect operator++(); //这是类的运算符的重载
friend rect operator++(rect &ob); //这是全局运算符的重载
};

rect operator++(rect &ob) {
ob.x1++, ob.y1++;
ob.x2++, ob.y2++;

return ob;
}

int main ( )
{
rect r(12, 20, 50, 40);
r.print();

rect obj;
obj = r++;
obj.print();

return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值