c++ primer plus 第15章友,异常和其他:15.1.2 友元成员函数

#c++ primer plus 第15章友,异常和其他:15.1.2 友元成员函数
提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:15.1.2 友元成员函数


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

15.1.2 友元成员函数

15.1.2 友元成员函数

从上一个例子中的代码可知,大多数 Remote 方法都是用Tv类的公有接口实现的。这意味着这些方法不是真正需要作为友元。事实上,唯一直接访问Tv成员的Remote方法是Remote:set_chan(),因此它是唯一需要作为友元的方法。确实可以选择仅让特定的类成员成为另一个类的友元,而不必让整个类成为友元,但这样做稍微有点麻烦,必须小心排列各种声明和定义的顺序。下面介绍其中的原因。让 Remote::set chan()成为 Tv类的友元的方法是,在Tv类声明中将其声明为友元:


class Tv
{
friend void Remote::set chan(Tv &t,int c);
...
}

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

// forward declaration
class Tv;

这样,排列次序应如下:

//forward declar
class Tv ;
class Remote{...} ;
class Tv {...}.

能否像下面这样排列呢?

class Remote;
class Tv{...};
class Remote{...};
//forward declaration

答案是不能。原因在于,在编译器在Tv类的声明中看到 Remote 的一个方法被声明为 Tv 类的友元之前,应该先看到 Remote类的声明和set_chan( )方法的声明。
还有一个麻烦。程序清单15.1的Remote 声明包含了内联代码,例如:

void onoff(Tv &t){t.onoff();}

由于这将调用 Tv的一个方法,所以编译器此时必须已经看到了 Tv类的声明,这样才能知道 Tv有哪些方法,但正如看到的,该声明位于 Remote声明的后面。这种问题的解决方法是,使 Remote 声明中只包含方法声明,并将实际的定义放在Tv类之后。这样,排列顺序将如下:

class Tv;//forward declarationclass Remote...;//Tv-using methods as prototypes only
class Tv{...}
//put Remote method definitions here

Remote 方法的原型与下面类似:

void onoff(Tv &t);

检查该原型时,所有的编译器都需要知道Tv是一个类,而前向声明提供了这样的信息。当编译器到达真正的方法定义时,它已经读取了Tv类的声明,并拥有了编译这些方法所需的信息。通过在方法定义中使用 inline 关键字,仍然可以使其成为内联方法。程序清单15.4列出了修订后的头文件。

程序清单 15.4 tvfm.h

// tvfm.h -- Tv and Remote classes using a friend member
#ifndef TVFM_H_
#define TVFM_H_

class Tv;                       // forward declaration

class Remote
{
public:
    enum State{Off, On};
    enum {MinVal,MaxVal = 20};
    enum {Antenna, Cable};
    enum {TV, DVD};
private:
    int mode;
public:
    Remote(int m = TV) : mode(m) {}
    bool volup(Tv & t);         // prototype only
    bool voldown(Tv & t);
    void onoff(Tv & t);
    void chanup(Tv & t);
    void chandown(Tv & t);
    void set_mode(Tv & t);
    void set_input(Tv & t);
    void set_chan(Tv & t, int c);
};

class Tv
{
public:
    friend void Remote::set_chan(Tv & t, int c);
    enum State{Off, On};
    enum {MinVal,MaxVal = 20};
    enum {Antenna, Cable};
    enum {TV, DVD};

    Tv(int s = Off, int mc = 125) : state(s), volume(5),
        maxchannel(mc), channel(2), 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 == Antenna)? Cable : Antenna;}
    void set_input() {input = (input == TV)? DVD : TV;}
    void settings() const;
private:
    int state;
    int volume;
    int maxchannel;
    int channel;
    int mode;
    int input;
};

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

如果在 tv.cpp 和 use_tv.cpp 中包含 tvfim.h 而不是 tvh,程序的行为与前一个程序相同,区别在于,只有一个Remote方法是 Tv 类的友元,而在原来的版本中,所有的 Remote 方法都是 Tv 类的友元。图 15.1 说明了这种区别。

在这里插入图片描述
本书前面介绍过,内联函数的链接性是内部的,这意味着函数定义必须在使用函数的文件中。在这个例子中,内联定义位于头文件中,因此在使用函数的文件中包含头文件可确保将定义放在正确的地方。也可以将定义放在实现文件中,但必须删除关键字inline,这样函数的链接性将是外部的。顺便说一句,让整个Remote 类成为友元并不需要前向声明,因为友元语句本身已经指出 Remote 是一个类:

friend class Remote;
  • 7
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值