C++友元函数和友元类用法详解(附带实例)
为了安全性,C++ 类中的成员变量一般都是隐藏的。但有时需要允许一些特殊函数能直接读写类的私有或保护成员变量,这就要通过 friend 关键字和友元机制。
下面来看一个例子。利用 CRectangle 类求解矩形的面积,当不使用友元函数时,代码如下:
下面是使用友元函数的情况:
C++ 提供了友元类和友元方法(又称为友元函数),以帮助访问其他类的私有成员。
当用户希望另一个类能够访问当前类的私有成员时,可以在当前类中将另一个类声明为友元类。例如,定义如下友元类:
例如,定义 CItem 类时,将 CList 类的某个函数定义为友元函数,就可以限制只能该函数访问 CItem 类的私有成员。
友元函数不仅可以是类的成员函数,还可以是全局函数。例如:
通过友元函数访问类对象中的成员时,不需要通过对象名。友元函数没有 this 指针,如果不通过对象名就无法找到类对象中的非 static 成员,也就无法访问。但是当它访问类对象中的 static 成员时,就可以不通过对象名访问。
下面来看一个例子。利用 CRectangle 类求解矩形的面积,当不使用友元函数时,代码如下:
#include <iostream> class CRectangle //定义CRectangle类 { protected: int m_iHeight; //定义保护成员变量m_iHeight int m_iWidth; //定义保护成员变量m_iWidth public: CRectangle() //定义构造函数CRectangle(),初始化矩形高、宽为0 { m_iHeight=0; m_iWidth=0; } //定义带参构造函数,根据矩形四个端点的坐标计算矩形的高和宽 CRectangle(int iLeftTop_x,int iLeftTop_y,int iRightBottom_x,int iRightBottom_y) { m_iHeight=iRightBottom_y-iLeftTop_y; m_iWidth=iRightBottom_x-iLeftTop_x; } int getHeight() //定义公共成员函数getHeight(),获取矩形的高 { return m_iHeight; } int getWidth() //定义公共成员函数getWidth(),获取矩形的宽 { return m_iWidth; } }; int ComputerRectArea(CRectangle & myRect) //定义函数ComputerRectArea(),计算矩形面积 { return myRect.getHeight()*myRect.getWidth(); //注意,这里只能引用公共成员函数 } int main() { CRectangle rg(0,0,100,100); //创建CRectangle类的对象rg并初始化 cout <<"Result of ComputerRectArea is :" << ComputerRectArea(rg)<< endl;//调用成员函数,返回矩形面积 }上述代码中,ComputerRectArea() 是普通函数,在计算矩形的高和宽时,只能引用 CRectangle 类中的公共成员函数 getHeight() 和 getWidth(),而不能直接引用保护成员变量 m_iHeight 和 m_iWidth。这是因为私有和保护成员对外是不可见的,不允许外部访问。
下面是使用友元函数的情况:
#include <iostream> class CRectangle //定义CRectangle类 { protected: int m_iHeight; //定义保护成员变量m_iHeight int m_iWidth; //定义保护成员变量m_iWidth public: CRectangle() //定义构造函数CRectangle(),初始化矩形高、宽为0 { m_iHeight=0; m_iWidth=0; } //定义带参构造函数,根据矩形四个端点的坐标计算矩形的高和宽 CRectangle(int iLeftTop_x,int iLeftTop_y,int iRightBottom_x,int iRightBottom_y) { m_iHeight=iRightBottom_y-iLeftTop_y; m_iWidth=iRightBottom_x-iLeftTop_x; } int getHeight() //定义公共成员函数getHeight(),获取矩形的高 { return m_iHeight; } int getWidth() //定义公共成员函数getWidth(),获取矩形的宽 { return m_iWidth; } friend int ComputerRectArea(CRectangle & myRect); //注意,此处声明友元函数ComputerRectArea() }; int ComputerRectArea(CRectangle & myRect) //定义友元函数ComputerRectArea(),计算矩形面积 { return myRect.m_iHeight*myRect.m_iWidth; //注意,这里可直接引用CRectangle类的私有或保护成员变量 } int main() { CRectangle rg(0,0,100,100); //创建CRectangle类的对象rg并初始化 cout <<"Result of ComputerRectArea is " << ComputerRectArea(rg)<< endl; //调用成员函数,返回矩形面积 }上述代码中,ComputerRectArea() 被声明为 CRectangle 类的友元函数,因此在计算矩形的高和宽时,可以直接引用其对象的保护成员变量 m_iHeight 和 m_iWidth。
C++友元类
类的私有方法只允许在该类中访问,其他类是不能访问的。但实际开发中,如果两个类的耦合度比较大,则通过一个类访问另一个类的私有成员会带来很大的便捷性。C++ 提供了友元类和友元方法(又称为友元函数),以帮助访问其他类的私有成员。
当用户希望另一个类能够访问当前类的私有成员时,可以在当前类中将另一个类声明为友元类。例如,定义如下友元类:
class CItem //定义 CItem 类 { private: char m_Name[128]; //定义私有成员变量 m_Name[128] void OutputName() //定义私有成员函数OutputName() { printf("%s\n",m_Name); } public: friend class CList; //声明CList类为自己的友元类 void SetItemName(const char *pchData) //定义公有成员函数,设置m_Name成员 { if (pchData != NULL) //判断指针是否为空 strcpy(m_Name,pchData); //赋值字符串 } CItem() //定义构造函数 { memset(m_Name,0,128); //初始化成员变量 m_Name } }; class CList //定义 CList 类 { private: CItem m_Item; //定义私有成员变量m_Item public: void OutputItem(); //定义公有成员函数OutputItem() }; void CList::OutputItem() //OutputItem()函数的实现代码 { m_Item.SetItemName("BeiJing"); //调用CItem类的公有方法 m_Item.OutputName(); //调用 CItem类的私有方法 }定义 CItem 类时,使用 friend 关键字将 CList 类声明为 CItem 类的友元类,这样 CList 类中的所有方法都可以访问 CItem 类中的私有成员。在 CList 类的 OutputItem() 方法中,通过 m_Item.OutputName() 调用 CItem 类的私有方法 OutputName()。
C++友元函数
当只允许类的某个成员函数访问当前类的私有成员,而不允许其他成员函数访问时,可以通过友元函数来实现。例如,定义 CItem 类时,将 CList 类的某个函数定义为友元函数,就可以限制只能该函数访问 CItem 类的私有成员。
class CItem; //前导声明 CItem类 class CList //定义 CList 类 { private: CItem *m_pItem; //定义私有成员变量m_pItem public: CList(); //定义默认构造函数 ~CList(); //定义析构函数 void OutputItem(); //定义OutputItem()成员函数 }; class CItem //定义 CItem 类 { friend void CList::OutputItem(); //声明OutputItem()为友元函数 private: char m_Name[128]; //定义私有成员变量 m_Name[128] void OutputName() //定义私有成员函数 OutputName() { printf("%s\n",m_Name); //输出成员变量信息 } public: void SetItemName(const char *pchData) //定义公有成员函数 SetItemName() { if (pchData != NULL) //判断指针是否为空 strcpy(m_Name,pchData); //赋值字符串 } CItem() //定义构造函数,进行初始化 { memset(m_Name,0,128); } }; void CList::OutputItem() //CList 类的 OutputItem()成员函数的实现 { m_pItem->SetItemName("BeiJing"); //调用CItem类的公有方法SetItemName() m_pItem->OutputName(); //在友元函数中访问 CItem类的私有方法 OutputName() } CList::CList() //CList类的默认构造函数 { m_pItem = new CItem(); } CList::~CList() //CList 类的析构函数,释放 m_pItem对象 { delete m_pItem; m_pItem = NULL; } int main(int argc, char *argv[]) //主函数 { CList list; //定义 CList 类的对象 list list.OutputItem(); //调用 list 对象的 OutputItem()方法 return 0; }上面的代码中定义 CItem 类时,使用 friend 关键字将 CList 类的 OutputItem() 设置为友元函数,在 list 对象的 OutputItem() 方法中访问 CItem 类的私有方法 OutputName()。程序运行结果为:
BeiJing
友元函数不仅可以是类的成员函数,还可以是全局函数。例如:
class CItem //定义 CItem类 { friend void OutputItem(CItem *pItem); //将全局函数OutputItem()定义为友元函数 private: char m_Name[128]; //定义私有成员变量 m_Name[128] void OutputName() //定义私有成员函数OutputName() { printf("%s\n",m_Name); } public: void SetItemName(const char *pchData) //定义公有成员函数SetItemName() { if (pchData != NULL) //判断指针是否为空 strcpy(m_Name,pchData); //赋值字符串 } CItem() //定义构造函数,初始化成员变量 { memset(m_Name,0,128); } }; void OutputItem(CItem *pItem) //定义全局函数OutputItem() { if (pItem != NULL) //判断参数是否为空 pItem->SetItemName("同一个世界,同一个梦想\n"); //调用CItem类的公有成员函数SetItemName() pItem->OutputName(); //调用CItem类的私有成员函数OutputName() } int main(int argc, char *argv[]) //主函数 { CItem Item; //定义CItem类的对象Item OutputItem(&Item); //通过全局函数访问CItem类的私有方法 return 0; }上面的代码中,定义全局函数 OutputItem(),在 CItem 类中将 OutputItem() 函数声明为友元函数,而 CItem 类中 OutputName() 函数的属性是私有的,对外是不可见的。因为 OutputItem() 是 CItem 类的友元函数,所以可以引用类中的私有成员。
通过友元函数访问类对象中的成员时,不需要通过对象名。友元函数没有 this 指针,如果不通过对象名就无法找到类对象中的非 static 成员,也就无法访问。但是当它访问类对象中的 static 成员时,就可以不通过对象名访问。