友元类:
什么时候希望一个类成为另一个类友元类呢?
举个例子,假设需要编写一个模拟电视和遥控器的简单程序,定义了一个Tv类和Remote类表示。这两个类之间存在某种关系,电视机并非遥控器,反之亦然,所以公有继承is-a关系并不适用。遥控器也并非电视机的一部分,反之亦然,因此包含或私有继承和保护继承的has-a关系也不适用。事实上,遥控器可以改变电视机的状态,这表明应将Remote类作为Tv类的一个友元类。友元类可以访问当前类的私有和保护成员。
友元函数:
#include<iostream>
#include<string>
using namespace std;
class Tv
{
enum { OFF, ON };//枚举类型没有占用内存(占用内存的只是变量)
enum { Antenna,Cable};
private:
int state;
int mode;
int channel;
int maxchannel;
public:
friend class Remote;//友元类前向声明
Tv(int s=OFF) :state(s) ,mode(Cable),channel(0),maxchannel(100){}
void onoff() { state ^= state; }
bool ison() { return state == ON; }
void set_mode(){ mode ^= mode; }
};
class Remote
{
private:
//
public:
Remote() {}
void onoff(Tv&t) { t.onoff(); }
void set_mode(Tv&t) { t.set_mode(); }
void set_channel(Tv&t, int c) { t.channel = c; }
};
void main()
{
Tv haier;
Remote r1;
r1.onoff(haier);
r1.set_mode(haier);
r1.set_channel(haier, 10);
cout << sizeof(Tv) << endl;//16字节
cout << sizeof(Remote) << endl;//1字节,内存大小最少是1字节(所以class Remote这里是1字节)
}
友元成员函数:
上述的代码中Remote的大多数方法是通过调用Tv的公有接口实现的。意味这些方法不是真正需要作为友元。事实上唯一直接访问Tv成员的Remote方法是Remote::set_channel( …),因此这是唯一一个需要作为友元的方法。
在Tv类中声明中将其声明为友元:
class Tv
{
friend void Remote::set_channel(Tv&t, int c);
...
};
这时候需要修改一下类的声明顺序:
class Tv;//前向声明
class Remote{...};
class Tv{...};
#include<iostream>
#include<string>
using namespace std;
class Tv;
class Remote
{
private:
public:
Remote() {}
void onoff(Tv&t) ;
void set_mode(Tv&t) ;
void set_channel(Tv&t, int c);
};
class Tv
{
enum { OFF, ON };
enum { Antenna,Cable};
private:
int state;
int mode;
int channel;
int maxchannel;
public:
friend void Remote::set_channel(Tv&t, int c);
Tv(int s=OFF) :state(s) ,mode(Cable),channel(0),maxchannel(100){}
void onoff() { state ^= state; }
bool ison() { return state == ON; }
void set_mode(){ mode ^= mode; }
};
inline void Remote::onoff(Tv & t){t.onoff();}
inline void Remote::set_mode(Tv & t){t.set_mode();}
inline void Remote::set_channel(Tv & t, int c){t.channel = c;}
void main()
{
Tv haier;
Remote r1;
r1.onoff(haier);
r1.set_mode(haier);
r1.set_channel(haier, 10);
}
还有一点需要注意:Remote类使用到Tv对象的方法,必须先声明,但不能给定义(原因:和编译器有关)。定义必须在后面再给出详细的定义,比较短的代码可以定义成内联函数。
其他用法:
1两个类可以是相互的友元
class Tv
{
friend class Remote;
public:
void buzz(Remote&r);
...
};
class Remote
{
friend class Tv;
public:
...
};
inline void Tv::buzz(Remote::r)
{
...
}
2共同的友元
当函数需要访问两个类的私有成员时,那么当前类可以是一个类的成员,同时是另一个类的友元,但是有时将函数作为两个类的友元更加合理:
class A;//前向声明
class B
{
friend void sync(A&a,const B&b);
friend void sync(B&b,const A&a);
...
}
class A
{
friend void sync(A&a,const B&b);
friend void sync(B&b,const A&a);
...
}
inline void sync(A&a,const B&b)
{
}
inline void sync(B&b,const A&a)
{
}