zoukankan      html  css  js  c++  java
  • c++友元函数和友元类详解

    我们在设计类时,一般将数据成员设计成私有的,体现面向对象的信息隐藏和封装性;这些私有的数据成员只能由类成员函数所访问,类外函数不能访问;当在某些情况下,我们可能又需要访问类对象的私有成员,那我们应该怎么办呢?

    为了能够访问类私有成员,我们可以采用友元函数,在c++中以关键字friend加以声明,友元可以包括友元函数和友元类;友元函数又可以分为友元函数和友元成员函数;其一般形式分别如下:

    友元函数:
    friend 函数返回类型 函数名(形参列表)
    形如:friend void Display(const CMyTime& time)

    友元成员函数:
    friend 函数返回类型 类型名::函数名(形参列表)
    形如:friend void CMyDate::Display(const CMyTime& time)

    友元类:
    friend 类型名
    形如friend CMyDate

    友元函数

    友元函数就是将类外的函数,并在本类中加以friend关键字声明,那么这个函数就是本类的友元函数;

    下面就将普通函数声明为友元函数;

    class CMyTime
    {
    public:
        CMyTime(int hour,int minute, int second);
        //全局函数Display是本类的友元函数,可以访问其私有数据成员
        friend void Display(const CMyTime& time);
    private:
        int m_Hour;
        int m_Minute;
        int m_Second;
    };
    
    CMyTime::CMyTime(int hour,int minute, int second)
    {
        this->m_Hour   = hour;
        this->m_Minute = minute;
        this->m_Second = second;
    }

    在这个例子中,Display函数是一个全局的普通函数,不属于任何类,没有this指针;我们将其设置为友元函数后就能访问CMyTime类的私有成员了,否则Display函数将会报错;

    需要注意的是,为了能够引出类的成员函数,这个友元函数的形参一般是类对象的引用或者指针;
    友元函数如下:

    void Display(const CMyTime& time)
    {
       //因为Display函数没有this指针,引用这些私有成员数据,需要指定对象
       cout << time.m_Hour   << ":"
            << time.m_Minute << ":"
            << time.m_Second << "
    "
            << endl;
    }

    测试代码如下:

    int _tmain(int argc, _TCHAR* argv[])
    {
        CMyTime time(12,13,14);
        Display(time);
        return 0;
    }

    运行结果:12:13:14

    友元成员函数

    通常情况下,类和类之间是相互隔离的,但有可能一个类中的成员函数需要访问另一个类中的私有成员,我们则可将类成员函数设置为友元函数,则可以到达这个目的;

    比如 有一个日期类(CMyDate)对象和一个时间类(CMyTime)对象,要求一次性输出其中的日期和时间;

    现在先设计一个CMyDate类,用成员函数Display完成输出日期和时间,具体实现如下:

    //提前引用声明,表示存在CMyTime类名,类的具体内容后续再声明
    //在没有具体声明CMyTime类的内容时,不允许定义对象和引用函数名
    //比如在引用声明class CMyTime之后,添加对象定义CMyTime time 编译器将报错;
    class CMyTime;  
    class CMyTime* pTime = NULL;
    //class CMyTime time; error 
    
    class CMyDate
    {
    public:
        //构造函数
        CMyDate(int year,int month, int day);    
        //成员函数,输出具体时间和日期
        void Display(const CMyTime& time) const; 
    private:
        int m_Year;
        int m_Month;
        int m_Day;
    };
    
    CMyDate::CMyDate(int year,int month, int day)
    {
        this->m_Year  = year;
        this->m_Month = month;
        this->m_Day   = day;
    }
    
    class CMyTime
    {
    public:
        CMyTime(int hour,int minute, int second);
        //将CMyDate类中Display函数是CMyTime的友元成员函数
        friend void CMyDate::Display(const CMyTime& time) const;
    private:
        int m_Hour;
        int m_Minute;
        int m_Second;
    };
    
    //CMyDate::Display函数实现需要放在类CMyTime声明之后,否则不清楚CMyTime类有哪些成员
    void CMyDate::Display(const CMyTime& time) const
    {   
       cout << m_Year  << "/"
            << m_Month << "/"
            << m_Day   << " " ;
    
       cout << time.m_Hour   << ":"
            << time.m_Minute << ":"
            << time.m_Second << "
    "
            << endl;
    }

    测试代码如下:

    int _tmain(int argc, _TCHAR* argv[])
    {
        CMyTime time(12,12,12);
        CMyDate date(2017,7,16);
        //time对象作为实参,输出时间,Display是CMyDate类成员函数,有this指针,作为日期的输出源;
        date.Display(time);
        return 0;
    }

    运行结果:2017/7/17 12:12:12

    这里需要特别说明c++允许对类作“提前引用”的声明,即只先声明类名,不包含类体

    若在当前函数中需要先引用(不是指引用符&)某个类对象作为形参,但这个类还未声明;我们可以在文件开头先进行类名声明,不声明类体,类体声明稍后再给出,如上述代码中的 class CMyTime

    在对一个类作了“提前引用声明”后,可以用该类的名字去定义指向该类型对象的指针或者引用,因为定义一个指针变量和引用与这个类大小没有关系,但是不能用于定义对象,定义对象需要声明类体后才行
    ,如上述代码中的class CMyTime* pTime = NULL和Display的形参;

    友元类

    在c++中我们还可以将一个类设置为另一个类的友元类,这样友元类中的函数都可以访问另一个类的所有成员数据,并且友元类是单向的且不具备传递性,比如类A是类B的友元类,类B是类C的友元类,不能推出类A是类C的友元类,以及类B是类A的友元类;
    友元类使用如下:

    class CMyDate
    {
    public:
        //构造函数
        CMyDate(int year,int month, int day);    
        //成员函数
        void Display(const CMyTime& time) const; 
    private:
        int m_Year;
        int m_Month;
        int m_Day;
    };
    
    class CMyTime
    {
    public:
        CMyTime(int hour,int minute, int second);
        //友元类, CMyDate是CMyTime的友元类
        friend  CMyDate;
    private:
        int m_Hour;
        int m_Minute;
        int m_Second;
    };

    测试代码如下:

    int _tmain(int argc, _TCHAR* argv[])
    {
        CMyTime time(12,12,12);
        CMyDate date(2017,7,16);
        //time对象作为实参,输出时间,Display是CMyDate类成员函数,有this指针,作为日期的输出源;
        date.Display(time);
        return 0;
    }

    运行结果:2017/7/17 12:12:12

    如果没有了friend关键字声明,在Display函数中将会报如下错误:

    error C2248: “CMyTime::m_Hour”: 无法访问 private 成员
    error C2248: “CMyTime::m_Minute”: 无法访问 private 成员
    error C2248: “CMyTime::m_Second”: 无法访问 private 成员

    友元利弊分析

    面向对象程序设计的一个基本原则就是封装性和信息隐蔽,而友元函数和友元类却可以访问其他类的私有成员,在一定程度上这是封装性的小破坏;
    因此进行类设计时,不推荐将整个类设置为友元类,只将必要成员函数或者普通函数设置为友元类;
    由于友元有助于数据共享,使代码更加简洁,但又违背封装性和信息隐蔽,因此进行代码开发时需要做好权衡;

  • 相关阅读:
    NOI2010 能量采集
    NOI2011 兔兔与蛋蛋游戏
    动态规划——min/max的单调性优化总结
    NOI2011 NOI嘉年华
    NOI2011 阿狸的打字机
    NOI2011 智能车比赛
    NOI2011 兔农
    NOI2012 魔幻棋盘
    NOI2012 美食节
    NOI2012 迷失游乐园
  • 原文地址:https://www.cnblogs.com/jinxiang1224/p/8468236.html
Copyright © 2011-2022 走看看