首页 > 编程笔记 > C++笔记 阅读:9

C++友元函数和友元类用法详解(附带实例)

为了安全性,C++ 类中的成员变量一般都是隐藏的。但有时需要允许一些特殊函数能直接读写类的私有或保护成员变量,这就要通过 friend 关键字和友元机制。

下面来看一个例子。利用 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 成员时,就可以不通过对象名访问。

相关文章