友元的作用:允许让友元类(友元函数)访问自己的私有属性
友元的创建方式:在类的声明(函数的声明)前面加上友元关键字friend,放在允许被访问私有属性的类的里面(不受访问权限限制,随便放,只不过一般我们放在类的最上面)。
一、全局函数做类的友元函数
class Building
{
friend void visit1(Building building);//visit1函数在类里面被声明为友元类
friend void visit2(Building& building);//visit2函数在类里面被声明为友元类
public:
Building()
{
parlor = "客厅";
bathroom = "卧室";
}
void Print_bathroom()
{
cout << "bathroom:" << bathroom << endl;
}
public:
string parlor;
private:
string bathroom;
};
void visit1(Building building)//临时对象访问私有属性
{
cout << "visit1正在访问bathroom:" << building.bathroom << endl;
}
void visit2(Building& building)//原对象在友元函数里访问私有属性
{
cout << "visit2正在访问bathroom:" << building.bathroom << endl;//error
building.bathroom = "卧室2";
}
int main()
{
Building building;
visit1(building);
building.Print_bathroom();
visit2(building);
building.Print_bathroom();//building私有属性在visit2函数里面已经被修改
return 0;
}
总结来说,不管是在主函数里创建的对象还是在友元全局函数里创建的临时对象,在友元全局函数里对象都可以为所欲为,可以随意访问私有属性,甚至对象在里面可以修改自己的私有属性。
也就是说,除了友元全局函数没有this指针之外,友元函数和类的成员函数对于类来说没什么区别。所以慎用友元。
二、一个类的成员函数做另一个类的友元函数
作用:让一个类的成员函数可以访问另一个类的私有属性。
class Building
{
public:
Building()
{
parlor = "客厅";
bathroom = "卧室";
}
public:
string parlor;
private:
string bathroom;
};
class Breather
{
public:
Breather()
{
building = new Building;
}
string Get_bathroom()
{
return building->bathroom;
}
Building* building;
};
上面的代码是不允许的。Get_bathroom()函数会报错,原因是Building的bathroom为私有属性,Buildine类的对象不能访问。那么我们如果允许这函数访问呢?那就告诉Building类,这个Get_bathroom函数为友元函数。
但是一个类的成员函数做另一个类的友元函数这个过程是屡屡碰壁的!
首先我们来看第一种代码:
class Building
{
public:
friend string Breather::Get_bathroom();//将类Breather的成员函数Get_bathroom()声明为Building的友元函数
Building()
{
parlor = "客厅";
bathroom = "卧室";
}
public:
string parlor;
private:
string bathroom;
};
class Breather
{
public:
Breather()
{
building = new Building;
}
string Get_bathroom()
{
return building->bathroom;
}
Building* building;
};
这种代码编译器通不过,看一下运行结果:
先抛开访问权限问题,第二个问题说Breather不是类或命名空间名称,这个问题很好想,因为我们在类Breather定义和声明前就用它了,编译器不知道这是什么东西,那么我们平常解决办法就是提前声明一下。那么我们在最上面加上class Breather;声明后,再运行出现以下问题:
这个问题应该是找不到Get_bathroom(),那么我们干脆直接把Breather类放在Building类之前定义。那么就在最前面加上class Breather;声明Breather是一个类。修改后代码如下:
class Building;
class Breather
{
public:
Breather()
{
building = new Building;
}
string Get_bathroom()
{
return building->bathroom;
}
Building* building;
};
class Building
{
public:
friend string Breather::Get_bathroom();
Building()
{
parlor = "客厅";
bathroom = "卧室";
}
public:
string parlor;
private:
string bathroom;
};
运行结果:
人傻了,先不说一直报错的另一个问题(bathroom不可访问,这个问题说明我们友元没有弄成功),就连这两个类的类名都搞定不了了。冷静一下之后,突然想明白了。
首先再来仔细来分析一下,第23行代码用到了类Building的构造函数,我们只是声明了Building是一个类,但是编译器这个时候并不知道这个类里面有什么东西,所以没办法调用Building的构造函数,所以building没办法构造出来,所以23行和27行都报错。那么,我们就把类Breather中用到Building类及Building构造的对象的语句都放在最后面,放到Building类的后面,这样到时候编译器就已经知道Building类里面有什么东西了。修改后的代码:
class Breather;
class Building;
class Breather
{
public:
Breather();//只留声明,定义放到类Building下面
string Get_bathroom();//只留声明,定义放到类Building下面
Building* building;
};
class Building
{
public:
friend string Breather::Get_bathroom();
Building()
{
parlor = "客厅";
bathroom = "卧室";
}
public:
string parlor;
private:
string bathroom;
};
/**************Breather类中用到类Building的两个函数定义***************/
Breather::Breather()
{
building = new Building;
}
string Breather::Get_bathroom()
{
return building->bathroom;
}
/******************************************************************/
int main()
{
Breather breather;
breather.Get_bathroom();
return 0;
}
运行结果:
终于成功了!
类的成员函数做另一个类的友元函数,总结来说注意以下几点:
1.成员函数被做友元的类,写在另一个类的上面。
2.另一个类的声明写在成员函数被做友元的类的上面。(为了简单,可把所有类都在最上面声明一下)。
3.所有用到其他类的成员的函数的定义都写在其他类的下面。(在本来的类中肯定要写声明)。
三、一个类做另一个类的友元类
class Building
{
friend class Breather;//声明Breather为友元类
public:
Building()
{
parlor = "客厅";
bathroom = "卧室";
}
public:
string parlor;
private:
string bathroom;
};
class Breather
{
public:
Breather()
{
building = new Building;
}
string Get_bathroom()
{
return building->bathroom;
}
Building* building;
};
void visit1(Breather& breather)
{
cout << "breaather 正在访问:" << breather.building->parlor << endl;
//cout << "breaather 正在访问:" << breather.building->bathroom << endl;//error1
}
int main()
{
Breather breather;
cout << "正在访问:" << breather.building->parlor << endl;
//cout << "正在访问:" << breather.building->bathroom << endl;//error
cout << "正在访问:" << breather.Get_bathroom() << endl;
visit1(breather);
return 0;
}
运行结果:
以上面代码为例,总结来说,就是Building创建的对象在Breather类里面可以为所欲为,可以随意访问和修改自己的私有属性,但是在Breather类外面不可以这样做,哪怕是以Breather类为中介创建的Building的对象。
友元函数作用是在这个友元函数内,Building的对象可以为所欲为;友元类作用是在这个友元类内,Building的对象可以为所欲为。在类外除非调用这两个类的成员函数,否则无法继续访问Building的私有属性。
上面代码发现visit函数里不可以直接访问Building的私有属性,但是如果把visit声明为友元函数,那么在visit里面这些对象也可以为所欲为了。下面代码是把Breather声明为友元类,把visit函数声明为友元函数:
class Building
{
friend class Breather;//将Breather声明为友元类
friend void visit1(Breather& breather);//将visit1声明为友元函数
public:
Building()
{
parlor = "客厅";
bathroom = "卧室";
}
string Get()
{
return bathroom;
}
public:
string parlor;
private:
string bathroom;
};
class Breather
{
public:
Breather()
{
building = new Building;
}
string Get_bathroom()
{
return building->bathroom;
}
Building* building;
};
void visit1(Breather& breather)
{
cout << "breather 正在访问:" << breather.building->parlor << endl;
cout << "breather 正在访问:" << breather.building->bathroom << endl;
}
int main()
{
Breather breather;
visit1(breather);
return 0;
}
运行结果:
visit1声明为Building的友元函数,对象在里面可以为所欲为。那么如果在Breather类为Building类的友元类的前提下,在Breather类里面将visit1声明为友元函数,那么在visit1里面可以用breather.building->bathroom吗?如下代码:
class Building
{
friend class Breather;//将Breather类声明为Building的友元类
public:
Building()
{
parlor = "客厅";
bathroom = "卧室";
}
string Get()
{
return bathroom;
}
public:
string parlor;
private:
string bathroom;
};
class Breather
{
friend void visit1(Breather& breather);//将visit1声明为友元Breather的函数
public:
Breather()
{
building = new Building;
}
string Get_bathroom()
{
return building->bathroom;
}
Building* building;
};
void visit1(Breather& breather)
{
cout << "breaather 正在访问:" << breather.building->parlor << endl;
cout << "breaather 正在访问:" << breather.building->bathroom << endl;//error!!!!!!!!!!!
}
int main()
{
Breather breather;
visit1(breather);
return 0;
}
答案是不可以!
捋一下关系:Breather为Building的友元类,visit1为Breather的友元函数。所以在Breather类里面,Building类的对象可以为所欲为,在visit1函数里面,Breather类的对象可以为所欲为。
友元赋予的权限不具有传递性。
在visit1里面Breather类的对象可以为所欲为,但是不能够用Breather类的对象为中介去调用Building类的对象为所欲为。