友元类
友元类所有的方法都可以访问原始类的私有成员和保护成员
声明
friend class ClassName
友元声明可以位于公有、私有或保护部分,其所在的位置无关紧要。由于Remote类提到了Tv类,
所以编译器必须了解Tv类后才能处理Remote类,为些最简单的方法是首先定义Tv类。也可以使用前向声明,稍后介绍
#include<iostream> using std::cout; using std::cin; using std::endl; class Tv { private: int mode; int volume; public: friend class Remote; Tv(){}; void Show() { cout<<"welcome Tv "<<mode<<endl; } void set_mode(int i){ mode=i; cout<<" mode = "<<mode<<endl; }; }; class Remote { private: int mode; public: void volup(Tv &t,int i) { t.volume=i; cout<<" current volume "<<t.volume<<endl; }; void ShowTv(Tv &t) { t.Show(); } }; int main() { Tv _t; _t.set_mode(3); Remote _r; _r.volup(_t,2); _r.ShowTv(_t); cin.get(); }
上边的例子中代码真正访问Tv成员的是Remote方法是volup,其它的方法不是真需要作为友元的。因此它是唯一需要作为友元的方法
确实可以选择仅让特定的类成员成为另一个类的友元,需不必须让整个类成为友元,但这样稍微有点麻烦,必须小心排列各种声明和定义的顺序。
让Remote::volup()成为Tv类的友元的方法,在Tv类声明中将其声明为友元
class
{
friend void Remote::set_chan(Tv & t,int c);
};
然而,要使编译器能够处理这条语句,它必须知道Remote的定义,否则就无法知道Remote是一个类,而set_chan是这个类的方法。
这意味着就将Remote的定义放到Tv的定义前面。Remote的方法提到了Tv对象,而这意味着Tv定义应当位于Remote定义之前。避开这种循环
依赖的方法是,使用前向声明(forward declaration)为此,需要在Remote定义的前面插入下面的语句:
class Tv
这样排列次序应如下:
class Tv;
class Remote{...};
class Tv {....};
还有一个麻烦。程序清单Remote声明包含了内联代码,例如:
void volup(Tv &t,int i)
{
t.volume=i;
cout<<" current volume "<<t.volume<<endl;
};
由于这将调用Tv的一个方法,所以编译器此时必须已经看到了Tv类的声明,这样才能知道Tv有哪些方法,但正如看到的,该声明位于Remote声明方法后
。这种问题的解决方法是,使用Remote声明中只包含方法声明,并将实际的定义放在Tv类之后。这样
Remote方法的原型与下面类似:
void volup(Tv &t,int i);
检查该原型时,所有的编译器都需要知道Tv是一个类,而前向声明提供了这样的信息。当编译器到达真正的方法定义时,它已经读取了Tv类的声明,
并拥有了编译这些方法所需的信息。能过在方法定义中使用inline关键字,仍然可以使其成为内联方法。
#include<iostream> using std::cout; using std::cin; using std::endl; class Tv; class Remote { private: int mode; public: void volup(Tv &t,int i); void ShowTv(Tv &t); }; class Tv { private: int mode; int volume; public: friend void Remote::volup(Tv & t,int c); Tv(){}; void Show() { cout<<"welcome Tv "<<mode<<endl; } void set_mode(int i){ mode=i; cout<<" mode = "<<mode<<endl; }; }; inline void Remote::volup(Tv & t,int i) { t.volume=i; cout<<" current volume "<<t.volume<<endl; }; inline void Remote::ShowTv(Tv & t) { t.Show(); }; int main() { Tv _t; _t.set_mode(3); Remote _r; _r.volup(_t,2); _r.ShowTv(_t); cin.get(); }
内联函数的链接性是内部的,这意味着函数定义必须在使用函数的文件中,这个例子中内联定义位于头文件中,因此在使用函数的文件中包含
头文件可确保将定义放在正确的地方。这可以将定义放在实现文件中,但必须删除关键字inline这样函数的链接性将是外部的
再看一个操作符重载的小例子
#include<iostream> class B { public : B() { myValue=2; std::cout<<"B init"<<std::endl; } ~B() { std::cout<<"B end"<<std::endl; } //这样做可以 /*B operator+(const B av) { B a; a.myValue=myValue+av.myValue; return a; }*/ //也可以这么做 friend B operator+(const B b1,const B b2); //------------------------------------------------ int GetMyValue() { return myValue; } //重载<< friend std::ostream& operator<<(std::ostream& os,B); private : int myValue; }; B operator+(const B b1,const B b2) { B a; a.myValue=b1.myValue+b2.myValue; return a; } std::ostream& operator<<(std::ostream& os,B b) { return os<<"重载实现:"<<b.myValue<<std::endl; } int main() { B b1; std::cout<<b1; B b2; B b3=b1+b2; std::cout<<b3<<std::endl; std::cin.get(); return 0; }