c++友元

尽管友元被授予从外部访问类的私有部分的权限,但他们并不与面向对象的编程思想相悖;相反他提高了公共接口的灵活性。

一、友元类
友元声明可以位于公有、私有活保护部分、其所在位置无关紧要
我直接贴出一个摘自< c++ primer plus >的例子来演示 c++ 友元类
其中 Remote 为 Tv的友元类。
Tv.h

#ifndef TV_H_
#define TV_H_

/*一个类 电视  */
class Tv
{
public:
    friend class Remote;  //Remote类可以访问Tv Privite 的私有部分
    enum {
        off,on   //开关  
    };
    enum 
    {
        MinVal,MaxVal=20   //音量
    };
    enum {
        Antena,Cable  //使用的天线、还是电缆
    };
    enum 
    {
        TV ,DVD   //工作模式
    };

    Tv(int s = off, int mc = 125) :state(s), volume(5), maxchannel(mc),
        channel(5), mode(Cable), input(TV) {}
    void onoff() { state = (state == on) ? off : on; }
    bool ison()const { return state == on; }
    bool volup();   //增大声音
    bool voldown(); //减小声音
    void chanup();  //频道 +
    void chandown();//频道 -
    void set_mode() { mode = (mode == Antena) ? Cable : Antena; }
    void set_input() { input = (input == TV) ? DVD : TV; }
    void settings()const; //显示所有设置


private:
    int state;   // 开或者 关
    int volume; // 音量
    int maxchannel;  //最大
    int channel;   //当前频道
    int mode;   //  广播还是 电缆
    int input; //Tv  或者 DVD
};

/*Remote 的定义 (遥控器) */
class Remote {
private :
    int mode; // 控制 TV 或 DVD
public:
    Remote(int m = Tv::TV) :mode(m) {}
    bool volup(Tv & t) { return t.volup(); }
    bool voldown(Tv & t) { return t.voldown(); }
    void onoff(Tv & t) { return t.onoff(); }
    void chanup(Tv & t) { return t.chanup(); }
    void chandown(Tv & t) { return t.chandown(); }
    void set_chan(Tv &t, int c) { t.channel = c; } //访问了Tv的私有成员
    void set_mode(Tv &t) { t.set_mode(); }
    void set_input(Tv &t) { t.set_input(); }
};
#endif // TV_H_

Tv.cpp

#include "stdafx.h"
#include "Tv.h"
#include <iostream>

bool Tv::volup() {
    if (volume < MaxVal) {
        volume++;
        return true;
    }
    else {
        return false;
    }
}

bool Tv::voldown() {
    if (volume > MinVal) {
        volume--;
        return true;
    }
    else {
        return false;
    }
}

void Tv::chanup() {
    if (channel < maxchannel) channel++;
    else channel = 1;
}

void Tv::chandown() {
    if (channel > 1) channel--;
    else channel = maxchannel;
}

void Tv::settings() const {
    using std::cout;
    using std::endl;
    cout << "TV is " << (state == off ? "off" : "on") << endl;
    if (state == on) {
        cout << "Volume setting =" << volume << endl;
        cout << "Channel setting = " << channel << endl;
        cout << "Mode = " << (mode == Antena ? "antenna" : "cable") << endl;
        cout << "Input = " << (input == TV ? "TV" : "DVD") << endl;
    }
}

测试代码:

#include "stdafx.h"
#include "tv.h"
#include <iostream>

int main()
{
    using std::cout;
    Tv s42;
    cout << "Initial settings for 42 \" Tv: \n";
    s42.settings();
    s42.onoff();
    s42.chanup();

    cout << " \n Adjusted settings for 42 \" Tv: \n";
    s42.chanup();
    cout << "\n Adjusted settings for 42 \" Tv: \n";
    s42.settings();

    Remote grey;
    grey.set_chan(s42, 10);
    grey.volup(s42);
    grey.volup(s42);
    cout << " \n s42 \" settings after using remote: \n";
    s42.settings();

    Tv s58(Tv::on);
    s58.set_mode();
    grey.set_chan(s58, 58);
    cout << " \n s58 \" setting: \n";
    s58.settings();

    system("pause");
    return 0;
}
运行结果:
Initial settings for 42 " Tv:
TV is off

 Adjusted settings for 42 " Tv:

 Adjusted settings for 42 " Tv:
TV is on
Volume setting =5
Channel setting = 7
Mode = cable
Input = TV

 s42 " settings after using remote:
TV is on
Volume setting =7
Channel setting = 10
Mode = cable
Input = TV

 s58 " setting:
TV is on
Volume setting =5
Channel setting = 58
Mode = antenna
Input = TV
请按任意键继续. . .

上述代码中将Remote类设置成为了Tv类的友元类,但事实上我们看到:唯一访问Tv的成员的方法是void set_chan(Tv &t, int c) { t.channel = c; } ,因此它是唯一需要友元的方法。因此不必让整个类成为友元,这就引出了我们下面要讲的的友元成员函数。

二、友元成员函数
我们要再Tv中将Remote::set_chan()设置成友元:

clas Tv
{
    friend void Remote::set_chan(Tv & t,int c ) ;
}

然而要使编译器能够处理这条语句,它必须知道Remote的定义。否则,它无法知道Remote是一个类。而 set_chan是这个类的方法。这意味着应将Remote的定义放到Tv的定义前面。Remote的方法提到了Tv对象,而意味着Tv定义应当位于Remote定义之前,避开这种循环依赖的方法是,使用前向声明。
所以应该这样:

class Tv ; //前向声明
class Remote{...}
class Tv {...}

这里还有一个麻烦就是:
Remote 包含了内联代码例如:void onoff(Tv &t) {t.onoff();};
由于这将调用Tv的一个方法,所以编译器此时已经看到了Tv类的声明,这样才能知道Tv有哪些方法,但正如看到的,该声明位于Remote声明的后面。这种问题的解决方法是:使用Remote声明中只包含方法声明,并将实际的定义放到Tv类之后。 所以最终应该这样:

class Tv;  //前向声明
class Remote {...} //如要用到Tv 只能是方法声明
class Tv{...}
//接着写Remote的定义
  • 这里通过方法定义中使用 inline关键字,仍然可以使方法称为内联方法

所以程序最终将tv.h改为:

#ifndef TV_H_
#define TV_H_
class Tv;  //前向声明
class Remote {

public:
    enum {
        off, on   //开关  
    };
    enum
    {
        MinVal, MaxVal = 20   //音量
    };
    enum {
        Antena, Cable  //使用的天线、还是电缆
    };
    enum
    {
        TV, DVD   //工作模式
    };

private:
    int mode; // 控制 TV 或 DVD
public:
    Remote(int m = TV) :mode(m) {}
    //用到了Tv  只能是声明
    bool volup(Tv & t);
    bool voldown(Tv & t);
    void onoff(Tv & t);
    void chanup(Tv & t);
    void chandown(Tv & t);
    void set_chan(Tv &t, int c);
    void set_mode(Tv &t);
    void set_input(Tv &t);
};

class Tv
{
public:
    friend void Remote::set_chan(Tv & t,int c);  //友元成员函数
    enum {
        off, on   //开关  
    };
    enum
    {
        MinVal, MaxVal = 20   //音量
    };
    enum {
        Antena, Cable  //使用的天线、还是电缆
    };
    enum
    {
        TV, DVD   //工作模式
    };
    Tv(int s = off, int mc = 125) :state(s), volume(5), maxchannel(mc),
        channel(5), mode(Cable), input(TV) {}
    void onoff() { state = (state == on) ? off : on; }
    bool ison()const { return state == on; }
    bool volup();   //增大声音
    bool voldown(); //减小声音
    void chanup();  //频道 +
    void chandown();//频道 -
    void set_mode() { mode = (mode == Antena) ? Cable : Antena; }
    void set_input() { input = (input == TV) ? DVD : TV; }
    void settings()const; //显示所有设置
private:
    int state;   // 开或者 关
    int volume; // 音量
    int maxchannel;  //最大
    int channel;   //当前频道
    int mode;   //  广播还是 电缆
    int input; //Tv  或者 DVD
};

inline bool Remote::volup(Tv & t) { return t.volup(); }
inline bool Remote::voldown(Tv & t) { return t.voldown(); }
inline void Remote::onoff(Tv & t) { return t.onoff(); }
inline void Remote::chanup(Tv & t) { return t.chanup(); }
inline void Remote::chandown(Tv & t) { return t.chandown(); }
inline void Remote::set_chan(Tv &t, int c) { t.channel = c; }
inline void Remote::set_mode(Tv &t) { return t.set_mode(); }
inline void Remote::set_input(Tv &t) { return t.set_input(); }
#endif // TV_H_


测试结果不变。
*另外:也可一个将内联函数放在tv.cpp中,但必须去掉inline关键字,这样函数的连接性将成为外部的。
三、其他友元关系
1、上面的代码表示的是Remote是Tv的友元。但我们有时也会用到2个类互相友元。即Remote是Tv的友元,同时 Tv又是Remote的友元
他们定义与下面类似:

class Remote
class Tv
{
friend clas Remote
public:
    void buzz(Remote & r) ;
    ...
}

class Remote
{
friend class Tv;
public:
    void Bool volup(Tv & t){t.volup();}
    ...
}
inline void Tv::buzz(Remote & r)
{
...
}

由于Remote的声明位于Tv声明的后面,所以可以在类的定义Remote::volup(),但Tv::buzz()方法必须在Tv声明的外部定义,使其位于Remote声明的外面。如果不希望buzz()是内联的,则应在一个单独的方法定义文件中定义它。
2、共同的友元。
需要使用友元的另一种情况是,函数需要访问两个类的私有数据。它可以是一个类的友元,同时是另一个类的友元。示例如下:

class Analyzer;
class Probe
{
    friend void sync (Analyzer & a,const Probe & p) ;
    friend void sync (Probe & p,const Analyzer & a);
    ...
};
class Analyzer
{
    friend void sync (Analyzer & a,const Probe & p) ;
    friend void sync (Probe & p,const Analyzer & a);
}
inline void sync (Analyzer & a,const Probe & p)
{
    ...
}
inline void sync (Probe & p,const Analyzer & a)
{
    ...
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值