15.1.2 友元成员函数(1)
从上一个例子中的代码可知,大多数Remote方法都是用Tv类的公有接口实现的。这意味着这些方法不是真正需要作为友元。事实上,唯一直接访问Tv成员的Remote方法是Remote::set_chan( ),因此它是唯一需要作为友元的方法。确实可以选择仅让特定的类成员成为另一个类的友元,而不必让整个类成为友元,但这样做稍微有点麻烦,必须小心排列各种声明和定义的顺序。下面介绍其中的原因。
让Remote::set_chan( )成为Tv类的友元的方法是,在Tv类声明中将其声明为友元:
然而,要使编译器能够处理这条语句,它必须知道Remote的定义。否则,它无法知道Remote是一个类,而set_chan是这个类的方法。这意味着应将Remote的定义放到Tv的定义前面。Remote的方法提到了Tv对象,而这意味着Tv定义应当位于Remote定义之前。避开这种循环依赖的方法是,使用前向声明(forward declaration)。为此,需要在Remote定义的前面插入下面的语句:
这样,排列次序应如下:
能否像下面这样排列呢?
答案是不能。原因在于,在编译器在Tv类的声明中看到Remote的一个方法被声明为Tv类的友元之前,应该先看到Remote类的声明和set_chan( )方法的声明。
还有一个麻烦。程序清单15.1的Remote声明包含了内联代码,例如:
由于这将调用Tv的一个方法,所以编译器此时必须已经看到了Tv类的声明,这样才能知道Tv有哪些方法,但正如看到的,该声明位于Remote声明的后面。这种问题的解决方法是,使Remote声明中只包含方法声明,并将实际的定义放在Tv类之后。这样,排列顺序将如下:
Remote方法的原型与下面类似:
检查该原型时,所有的编译器都需要知道Tv是一个类,而前向声明提供了这样的信息。当编译器到达真正的方法定义时,它已经读取了Tv类的声明,并拥有了编译这些方法所需的信息。通过在方法定义中使用inline关键字,仍然可以使其成为内联方法。程序清单15.4列出了修订后的头文件。
程序清单15.4 tvfm.h