16.0 前言
生活中你的家有客厅(Public),有你的卧室(Private),
客厅所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去。
但是也有例外,你也可以允许你的好闺蜜好基友进去。
在程序里,有些私有属性也想让类外的一些特殊函数或者类进行访问, 就需要用到友元的技术。
友元的目的就是让一个函数或者类访问另一个类中私有成员。
友元的关键字为friend
。
友元的三种实现:
- 全局函数做友元
- 类做友元
- 成员函数做友元
16.1 全局函数做友元
在全局函数中调用哪个类的私有成员,就使用关键字friend
将此函数在那个类中声明即可。
语法:
friend 函数名(参数列表);
示例:
#include<iostream>
using namespace std;
class Building
{
friend void goodGay2(Building& building); //声明此全局函数是一个友元
public:
Building()
{
m_SittingRoom = "客厅";
m_Bedding = "卧室";
}
public:
string m_SittingRoom; //客厅
private:
string m_Bedding; //卧室
};
//全局函数
void goodGay(Building& building)
{
cout << "好基友全局函数正在访问:" << building.m_SittingRoom << endl;
//cout << "好基友全局函数正在访问:" << building.m_Bedding << endl; 无法访问私有成员的
}
void goodGay2(Building& building)
{
cout << "好基友全局函数正在访问:" << building.m_SittingRoom << endl;
cout << "好基友全局函数正在访问:" << building.m_Bedding << endl; //因为被声明了,所以可以访问
}
void test01()
{
Building building;
goodGay(building);
goodGay2(building);
}
int main()
{
test01();
system("pause");
return 0;
}
16.2 类做友元
在A类中调用B类的私有成员,就使用关键字friend
将A类在B类中声明即可。
语法:
friend class 类名;
示例:
#include<iostream>
using namespace std;
//这里使用另外一种函数的写法
//函数声明在类中,在类外定义,只要在全局区写就可以,需要指出函数的所属域(属于哪个类)
class Building2
{
friend class GoodGay; //声明GoodGay 是Building2 的一个友元类
public:
Building2();
string m_SettingRoom;
private:
string m_BedRoom;
};
class GoodGay
{
public:
void visit(); //参观函数,访问Building2中的属性
GoodGay();
Building2* building; //building是一个指针
};
Building2::Building2()
{
m_SettingRoom = "客厅";
m_BedRoom = "卧室";
}
void GoodGay::visit()
{
cout << "好基友类正在访问:" << building->m_SettingRoom << endl;
cout << "好基友类正在访问:" << building->m_BedRoom << endl;
}
GoodGay::GoodGay()
{
//创建一个建筑物的对象
building = new Building2; //从而调用Building2的构造函数
}
void test2()
{
GoodGay gg;
gg.visit();
}
int main()
{
test2();
system("pause");
return 0;
}
16.3 成员函数做友元
有时, 我们并不想使整个类A都成为类B的友元, 而是仅仅想使A类中的某个成员函数成为B类的友元, 这时可以在B类中使用下面的语法:
语法:
friend A::函数名(参数列表);
示例:
#include<iostream>
using namespace std;
class Building3;
class GoodGay2
{
public:
GoodGay2();
void visit(); //使visit可以访问Building3中的成员
void visit2(); //visit2不可以访问Building3中的成员
Building3* building;
};
class Building3
{
friend void GoodGay2::visit(); //要声明visit()是友元,必须将它定义在上面
public:
Building3();
string settingRoom;
private:
string BedRoom;
};
Building3::Building3()
{
this->settingRoom = "客厅";
this->BedRoom = "卧室";
}
GoodGay2::GoodGay2() //创建一个对象, 和实例化是相同的,所以需要卸载Building类定义好的后面
{
building = new Building3();
}
void GoodGay2::visit()
{
cout << "visit函数正在访问: " << building->settingRoom << endl;
cout << "visit函数正在访问: " << building->BedRoom << endl;
}
void GoodGay2::visit2()
{
cout << "visit2函数正在访问: " << building->settingRoom << endl;
}
int main()
{
GoodGay2 gg;
gg.visit();
gg.visit2();
system("pause");
return 0;
}
注意:这里必须使用类内声明类外定义的方法定义visit函数。
- 我们需要在Building3类中声明GoodGay2类中的成员函数visit是友元,所以需要首先在GoodGay2类中声明或者定义函数visit。
- 这不是将整个GoodGay2类声明为友元,也就是说代码要在GoodGay2类中寻找这个函数visit,因此我们需要将GoodGay2类定义在Building3之前。
- 因为我们的visit函数要访问Building3中的成员且类必须首先被定义,然后才能用引用或者指针访问其成员。因此需要将Building3类定义在visit函数之前,所以只能使用类内声明类外定义的方法,将visit函数首先在GoodGay2类中声明,然后定义Building3类,再使用类外定义的方法定义visit函数。
类的声明相关资料:
- 不完全类型(只声明的类)只能在非常有限的情况下使用:可以定义指向这种类型的指针或引用,也可以作为一个已经声明(但没有定义)的函数的参数或返回类型。
- 对于一个类来说,在创建它的对象前必须首先完成类的定义,而不能仅仅被声明。否则编译器就无法了解这样的对象需要多少存储空间。类似的,类也必须首先被定义,然后才能用引用或者指针访问其成员。
简而言之:
- 如果在一段代码中使用了A类实例化对象(为堆区开辟对象)或者成员变量、成员函数,那么A类必须在这段代码之前定义;
- 如果这段代码只使用A类来定义指针或者函数参数中的数据类型那么A类可以在这段代码上面声明,而在下面定义。
参考资料: C++中的类——类的定义和声明