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 成员

    友元利弊分析

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

  • 相关阅读:
    Ubuntu adb devices :???????????? no permissions (verify udev rules) 解决方法
    ubuntu 关闭显示器的命令
    ubuntu android studio kvm
    ubuntu 14.04版本更改文件夹背景色为草绿色
    ubuntu 创建桌面快捷方式
    Ubuntu 如何更改用户密码
    ubuntu 14.04 返回到经典桌面方法
    ubuntu 信使(iptux) 创建桌面快捷方式
    Eclipse failed to get the required ADT version number from the sdk
    Eclipse '<>' operator is not allowed for source level below 1.7
  • 原文地址:https://www.cnblogs.com/jinxiang1224/p/8468237.html
Copyright © 2011-2022 走看看