友元概述
在讲述类的内容时说明了隐藏数据成员的好处,但是有些时候,类会允许有一些特殊的函数直接读写其私有数据成员。
使用friend关键字可以让特定的函数或者别的类的所有成员函数对私有数据成员进行读写。这既可以保持数据的私有性,又能够使特定的类或函数直接访问私有数据。
有时候,普通函数需要直接访问一个类的保护或私有数据成员。如果没有友元机制,则只能将类的数据成员声明为公共的,从而任何函数都可以无约束地访问它。
普通函数需要直接访问类的保护或私有数据成员的原因主要是为了提高效率。
例如,没有使用友元函数的情况如下:
代码:
#include<iostream>
using namespace std;class Rectangle
{
public:Rectangle(){height = 0;width = 0;}Rectangle(int top1,int top2,int bottom1,int bottom2){height = bottom2 - top2;width = bottom1 - top1;}int getHeight(){return height;}int getWidth(){return width;}protected:int height;int width;
};int rectArea(Rectangle &myRect) //不是友元函数的定义
{return myRect.getHeight() * myRect.getWidth();
}int main()
{Rectangle rg(0,0,100,100);cout << "Result of rectArea is:" << rectArea(rg) << endl;return 0;
}
结果:
在代码中可以看到,rectArea函数的定义只能对类中的函数进行引用,因为类中的函数属性都为共有属性,对外是可见的,但是数据成员的属性为受保护属性,对外是不可见的,所以只能使用共有成员函数得到想要的值。
下面来看一下使用友元函数的情况:
代码:
#include<iostream>
using namespace std;class Rectangle
{
public:Rectangle(){height = 0;width = 0;}Rectangle(int top1,int top2,int bottom1,int bottom2){height = bottom2 - top2;width = bottom1 - top1;}int getHeight(){return height;}int getWidth(){return width;}friend int rectArea(Rectangle &myRect); //声明为友元函数protected:int height;int width;
};int rectArea(Rectangle &myRect) //友元函数的定义
{return myRect.height * myRect.width;//return myRect.getHeight() * myRect.getWidth(); 用这个效果是一样的,不过友元函数多了一个可以直接访问这些隐藏数据成员
}int main()
{Rectangle rg(0,0,100,100);cout << "Result of rectArea is:" << rectArea(rg) << endl;return 0;
}
结果:
在rectArea函数的定义中可以看到使用Rectangle的对象可以直接引用其中的数据成员,这是因为在Rectangle类中将rectArea函数声明为友元了。
从中可以看到使用友元保持了Rectangle类中数据的私有性,起到了隐藏数据成员的好处,又使得特定的类或函数可以直接访问这些隐藏数据成员。
友元类
对于类的私有方法,只有在该类中允许访问,其它类是不能访问的。但是在开发程序时,如果两个类的耦合度比较紧密,能够在一个类中访问另一个类的私有成员会带来很大的方便。C++提供了友元类和友元方法来实现访问其它类的私有成员。当用户希望另一个类能够访问当前类的私有成员时,可以在当前类中将另一个类作为自己的友元类,这样在另一个类中就可以访问当前类的私有成员了。例如:
#include<iostream>
#include<string.h>
using namespace std;class Item
{
private:char name[100];void outputName(){cout << name << endl;}public:friend class List;void setItemName(const char *pchData){if(pchData != NULL){strcpy(name,pchData);}}Item(){memset(name,0,128);}
};class List
{
private:Item item;
public:void outputItem(){item.setItemName("ShangHai");item.outputName();}
};int main()
{List i1;i1.outputItem();return 0;
}
结果:
在定义Item类时,使用friend关键字将List类定义为Item类的友元,这样List类中的所有方法都可以访问Item类中的私有成员。在List类的outputItem方法中,语句“item.outputName();”演示了调用Item类的私有方法outputName。
友元方法
在开发程序时,有时需要控制另一个类对当前类的私有成员的方法。例如,假设需要实现只允许List类的某个成员访问Item类的私有成员,而不允许其它成员函数访问Item类的私有数据。这可以通过定义友元函数来实现。在定义Item类时,可以将List类的某个方法定义为友元方法,这样就限制了只有该方法允许访问Item类的私有成员。
例子:
#include<iostream>
using namespace std;class Item;
class List
{
private:Item *m_pItem;
public:List();~List();void OutputItem();
};class Item
{friend void List::OutputItem();
private:char m_Name[100];void OutputName(){cout << m_Name << endl;}
public:void SetItemName(const char *pchData){if(pchData !=NULL){strcpy(m_Name,pchData);}}Item(){memset(m_Name,0,128);}
};
void List::OutputItem()
{m_pItem->SetItemName("ShangHai");m_pItem->OutputName();
}
List::List()
{m_pItem = new Item();
}
List::~List()
{delete m_pItem;m_pItem = NULL;
}
int main()
{List list;list.OutputItem();return 0;
}
结果:
上述代码中,在定义Item类时,使用friend关键字将List类的OutputItem方法设置为友元函数,在List类的OutputItem方法中访问了Item类的私有方法OutputName。
对于友元函数来说,不仅可以是类的成员函数,还可以是一个全局函数。例如:
#include<iostream>
using namespace std;class Item
{friend void OutputItem(Item *pItem);
private:char m_Name[100];void OutputName(){cout << m_Name << endl;}public:void SetItemName(const char *pchData){if(pchData != NULL){strcpy(m_Name,pchData);}}Item(){memset(m_Name,0,128);}
};void OutputItem(Item *pItem)
{if(pItem != NULL){pItem->SetItemName("by Johnson666");pItem->OutputName();}
}int main()
{Item item;OutputItem(&item);return 0;
}
结果:
在上面的代码中,定义全局函数OutputItem,在Item类中使用friend关键字将OutputItem函数声明为友元函数。而Item类中OutputName函数的属性是私有的,对外是不可见的。因为OutputItem是Item类的友元函数,所以可以引用类中的私有成员。