前言
作用:友元类的所有方法 和 友元函数 可以访问原始类的私有成员和保护成员。
友元类的声明:friend class [classname];
。
友元函数的声明:friend [functype] [funcname] ([arglist]);
。
声明位置:友元声明的位置并不重要,可以public、privated和protected——没有任何区别。
什么时候考虑友元:你不是我(非同一个类),也不是我身体的一部分(不是类的成员),但你可以用我的东西(被保护【protected】起来、被保密【privated】起来的成员)。
如果一个男孩已经有女友了,同时他找了一个小三。对其他人来说,男孩的钱是私有财产,但以女友和小三的身份,总是可以轻易花掉男孩的钱。以此,完成一个简单友元类和友元函数的使用示例:
#include <iostream>
using namespace std;
/* 前置声明 */
class Girl;
void inamorata();
class Boy {
private:
int money;
public:
Boy(int m): money(m) {}
void show_moeny() { cout << "boy还剩" << money << "元" << endl;}
friend class Girl; /* 友元类 */
friend void inamorata(Boy& boy); /* 友元函数 */
};
class Girl { /* 男孩的女友 */
public:
void go_shopping(Boy& boy) {
boy.money--;
cout << "girl完成购物" << endl;
}
};
void inamorata(Boy& boy) /* 男孩的情妇 */
{
boy.money--;
cout << "inamorata完成购物" << endl;
}
int main() {
Boy boy(100); /* 初定男孩有100块钱 */
Girl girl;
girl.go_shopping(boy); /* 女孩购物 */
boy.show_moeny();
cout << endl << "----split line-----" << endl << endl;
inamorata(boy); /* 情妇购物 */
boy.show_moeny();
return 0;
}
// 输出:
girl完成购物
boy还剩99元
----split line-----
inamorata完成购物
boy还剩98元
(之所以把inamorata设计成函数,女孩设计成类,是因为我觉得小三见不得光,身份上应该低于正牌女友)
从输出结果可以看到,作为友元类的Girl,和友元函数的inamorata,它们可以访问原始类Boy的私有成员,也就是说,这就是友元存在的意义。
互为友元
上面的例子比较简单,正确地说,友元本身就是一个比较简单的概念。在使用友元的时候,需要着重注意的应该是:声明与定义的先后顺序。
现在假设一个场景:有一对夫妻,他们关系很好,所以可以相互使用彼此的所有东西。男人可以用女人的化妆品,女人可以穿男人的体恤衫。我们同时也假设,化妆品和体恤衫是私有的。现在利用友元类实现这个过程:
class Woman;
class Man {
private:
string wearing = "体恤衫";
public:
void use(Woman& woman) { cout << "男人使用了" << woman.makeup << endl;}
friend class Woman;
};
class Woman {
private:
string makeup = "护手霜";
public:
void use(Man& man) { cout << "女人使用了" << man.wearing << endl;}
friend class Man;
};
先说明:上面的代码是错误的。
如果尝试编译上述代码,会得到这样错误:error C2027: 使用了未定义类型“Woman”
。上述代码里,对Woman做了前置声明,这是因为在Man类中声明了友元类(friend class Woman;
),用到了Woman。然而,Man中不但用到了Woman,还用到了Woman中的私有成员:makeup。不能用makeup吗?当然可以,这是友元存在的意义。但用的不是时候,因为在Man中使用makeup时,Woman只是被声明了,还未被定义,在编译器看来,它认为Woman中并不存在makeup ,因此报错。
我们可以等Woman定义好后,再让Man的成员函数use()使用makeup,就像下边这样:
class Woman;
class Man {
private:
string wearing = "体恤衫";
public:
void use(Woman& woman);
friend class Woman;
};
class Woman {
private:
string makeup = "护手霜";
public:
void use(Man& man) { cout << "女人使用了" << man.wearing << endl;}
friend class Man;
};
inline /* 在类外定义 */
void Man::use(Woman& woman) { cout << "男人使用了" << woman.makeup << endl;}
友元成员函数
使用友元类以后,就意味着友元类的所有成员函数都可以访问原始类的私有成员、保护成员。可能我们并不想这样——因为现实世界很少允许这样。比如一对夫妻,丈夫允许妻子任意花掉他的工资,但不愿意妻子使用他的手机。这个时候我们就不能鲁莽的把Woman作为Man的友元类,而应该有选择的把Woman中的成员函数设置为Man中的友元函数。
class Man; /* 前置声明 */
class Woman{
public:
void use_money_of_man(Man& man);
void use_phone_of_man(Man& man);
};
class Man {
private:
string money = "男人的钱";
string phone = "男人的手机";
/* 指定Woman中的use_money_of_man为Man的友元函数 */
friend void Woman::use_money_of_man(Man& man);
};
inline /* 不可以直接在Woman中定义 */
void Woman::use_money_of_man(Man& man)
{
cout << "女人花掉了" << man.money << endl;
}
inline
void Woman::use_phone_of_man(Man& man)
{
/* 此时不能使用man.phone,否则编译不通过 */
cout << "女人不被允许使用男人的手机" << endl;
}
int main()
{
Man man;
Woman woman;
woman.use_money_of_man(man);
woman.use_phone_of_man(man);
}
// 输出:
女人花掉了男人的钱
女人不被允许使用男人的手机
也请注意此次声明与定义的先后顺序。
共同友元
现实中,也存在两个类拥有共同友元函数的情况,因为这个函数既不应该是A类的成员函数,也不应该是B类的成员函数。比方说一对夫妻生下了一个孩子,这个孩子是独立存在的,也不是男人身体的一部分,也不是女人身体的一部分,但他又可以花男人的钱,也可以花女人的钱。所以这个孩子对于男人和女人来说,就是共同的友元函数。
class Man;
class Woman{
private:
string money = "女人的钱";
friend void son(Man& man, Woman& woman);
};
class Man {
private:
string money = "男人的钱";
friend void son(Man& man, Woman& woman);
};
void son(Man& man, Woman& woman)
{
cout << "花" << man.money << endl;
cout << "花" << woman.money << endl;
}
int main()
{
Man man;
Woman woman;
son(man, woman);
}
// 输出:
花男人的钱
花女人的钱
还不快抢沙发