友元
我们来看最初的Sales_data版本。
struct Sales_data {
Sales_data() = default; //自定义的默认构造函数
Sales_data(const string& s) :_bookNo(s) {} //有参构造函数
Sales_data(const string& s, unsigned n, double p) :
_bookNo(s), _sold(n), _revenue(p) {} //有参构造函数
Sales_data(const Sales_data& rhs) : //拷贝构造函数
_bookNo(rhs._bookNo),
_sold(rhs._sold),
_revenue(rhs._revenue) {}
Sales_data& combine(const Sales_data&); //+=
double avgPrice()const; //求平均价格
string _bookNo;
unsigned _sold = 0; //类内初始值
double _revenue = 0.0; //类内初始值
};
Sales_data& Sales_data::combine(const Sales_data& rhs) {
_sold += rhs._sold;
_revenue += rhs._revenue;
return *this;
}
Sales_data add(const Sales_data& lhs, const Sales_data& rhs) {
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
double Sales_data::avgPrice()const {
if (_sold)
return _revenue / _sold;
else
return 0;
}
ostream& print(ostream& os, const Sales_data& s) {
os << s._bookNo << " " << s._sold << " "
<< s.avgPrice() << " " << s._revenue << endl;
return os;
}
istream& read(istream& is, Sales_data& item) {
double price = 0;
is >> item._bookNo >> item._sold >> price;
item._revenue = price * item._sold;
return is;
}
此时,Sales_data的数据成员都是public可访问的,非类内函数可以直接访问到类中的数据成员。但经过封装之后,数据成员编程private,我们的read、print和add函数就无法正常编译了。
*类可以允许其他类或者函数访问它的私有成员,方法是用友元令其他类或者函数成为它的友元。
友元一共有三种实现方法:全局函数做友元、类做友元、成员函数做友元。
1.成员函数做友元
如果把一个全局函数作为类的友元,只要在类中任意位置增加一个friend关键字开始的函数声明即可。一般来说,最好在类定义的开始集中声明友元。
下面我们利用友元技术将上述封装后的Sales_data实现。
class Sales_data {
public:
//类开头集中声明友元
friend Sales_data add(const Sales_data& lhs, const Sales_data& rhs);
friend ostream& print(ostream& os, const Sales_data& s);
friend istream& read(istream& is, Sales_data& item);
Sales_data() = default; //自定义的默认构造函数
Sales_data(const string& s) :_bookNo(s) {} //有参构造函数
Sales_data(const string& s, unsigned n, double p) :
_bookNo(s), _sold(n), _revenue(p) {} //有参构造函数
Sales_data(const Sales_data& rhs) : //拷贝构造函数
_bookNo(rhs._bookNo),
_sold(rhs._sold),
_revenue(rhs._revenue) {}
Sales_data& combine(const Sales_data&); //+=
double avgPrice()const; //求平均价格
private:
string _bookNo;
unsigned _sold = 0; //类内初始值
double _revenue = 0.0; //类内初始值
};
Sales_data& Sales_data::combine(const Sales_data& rhs) {
_sold += rhs._sold;
_revenue += rhs._revenue;
return *this;
}
Sales_data add(const Sales_data& lhs, const Sales_data& rhs) {
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
double Sales_data::avgPrice()const {
if (_sold)
return _revenue / _sold;
else
return 0;
}
ostream& print(ostream& os, const Sales_data& s) {
os << s._bookNo << " " << s._sold << " "
<< s.avgPrice() << " " << s._revenue << endl;
return os;
}
istream& read(istream& is, Sales_data& item) {
double price = 0;
is >> item._bookNo >> item._sold >> price;
item._revenue = price * item._sold;
return is;
}
2.用类做成员函数
如果一个类制定了友元类,则友元类的成员函数可以访问此类包括非公由成员在内的所有成员。
考虑如下场景:你的房子有客厅、卧室,你有一个好朋友想参观你的房子,平常你只想给别人参观你的客厅不想被他参观你的卧室,但是他是你的好基友你可以给他看你的卧室。
思路:房子可以作为一个只有数据成员的类,类中有卧室和客厅两个数据成员。你的朋友作为一个类,这个类有一个成员函数有一个数据成员,参观的动作作为一个成员函数,参观的房子作为一个数据成员。
//main.cpp
#include<iostream>
using namespace std;
#include<string>
//房子类
class GoodGay;
class Building{
friend GoodGay;
public:
Building():_sittingRoom("客厅"),_bedRoom("卧室"){}
string _sittingRoom;
private:
string _bedRoom;
};
//好基友类
class GoodGay{
public:
GoodGay(){
_build=new Building;
}
~GoodGay(){
delete _build;
}
string visit(){
return _build->_bedRoom;
}
Building *_build;
};
int main() {
GoodGay g;
cout << g.visit();
return 0;
}
输出结果为:
注意在Building类定义之前声明了GoodGay类,是因为Building类中声明了GoodGay友元,编译器不知道GoodGay,所以需要在Building类之前声明GoodGay是个类。
3.令成员函数作为友元
要想令某个成员函数作为友元,如A类的成员函数f()想访问B类的数据成员,我们必须按如下方式设计程序
- 先声明A类,定义B类,在b类中声明f(),但不能定义它。
- 定义A类。
- 定义clear。
在上述场景中,我们朋友想借住你的卧室,但你不想啊,所以你的朋友只能参观你的卧室,但不能住你的卧室。
#include<iostream>
using namespace std;
class Building;
class GoodGay{
public:
GoodGay();
void visit();//让visit函数可以访问Building中私有成员
void visit2();//让visit2函数不可以访问Building中私有成员
Building* building;
};
class Building{
//告诉编译器 GoodGay类下的visit成员函数作为本类的好朋友,可以访问私有成员
friend void GoodGay::visit();
public:
Building();
string m_SittingRoom;//卧室
private:
string m_BedRoom;//卧室
};
//类外实现成员函数
Building::Building(){
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
GoodGay::GoodGay(){
building = new Building;
}
void GoodGay::visit(){
cout << "visit函数正在访问:" << building->m_SittingRoom << endl;
cout << "visit函数正在访问:" << building->m_BedRoom << endl;
}
void GoodGay::visit2(){
cout << "visit函数正在访问:" << building->m_SittingRoom << endl;
//cout << "visit函数正在访问:" << building->m_BedRoom << endl;
}
int main(){
GoodGay gg;
gg.visit();
gg.visit2();
system("pause");
return 0;
}
输出结果为: