[本节内容]
友元函数
友元类
内部类
注意:友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
1.友元函数
首先大家看下面的代码:
class A
{
public:
A(int a = 10)
{
_a = a;
}
private:
int _a;
};
int main()
{
A a;
cout << a << endl;
system("pause");
return 0;
}
大家发现编译不通过,因为 cout << a << endl;
这里的a是一个对象,无法用<<输出。这时我们就想尝试重载operator<<去输出对象。于是就对代码进行修改:
class A
{
public:
A(int a = 10)
{
_a = a;
}
void operator<<(ostream &out)
{
out << _a;
}
private:
int _a;
};
int main()
{
A a;
cout << a << endl;
system("pause");
return 0;
}
这里大家又发现,还是编译错误!因为cout的输出流对象和成员函数中隐含的this指针在抢占第一个参数的位置,this指针默认是第一个参数就是左操作数,但是实际使用中cout 才应该是第一个形参对象。虽然我们改为用 a.operator<<(cout) ;或者 a << cout;
输出对象内容是可以完成的,但是这里与我们期望的cout 用法相反。所以最好的办法不是将operator<<重载为成员函数。
因此我们就想要不把operator<< 重载成全局函数。但是不用实践都知道是错误的。因为这样的话,就会导致类外无法访问成员。那么解决的办法就是把它写成类的友元函数!!
class A
{
public:
friend ostream & operator<<(ostream& _cout, const A& a);
A(int a = 10)
{
_a = a;
}
private:
int _a;
};
ostream & operator<<(ostream& _cout, const A& a)
{
_cout << a._a;
return _cout;
}
int main()
{
A a;
cout<<a<<endl;
system("pause");
return 0;
}
由此看出,友元函数是可以直接访问类的私有成员(保护成员)的非成员函数,它是定义在类外部的普通函数,不属于任何类, 但需要在类的内部声明,并且在声明时需要加上friend关键字。
格式: friend 类型 函数名 (形式参数);
注:友元函数没有this指针,它的参数有以下情况;
a.要访问非静态成员时,需要对象做参数
b.要访问静态成员或全局变量时,直接成员做参数
下面是Date类中,operator>>和operator<<的重载
class Date
{
public:
friend ostream &operator <<(ostream& _cout, const Date& d); //输出,所有加const表示只读数据
friend istream &operator >>(istream& _cin, Date& d); //输入,允许改写数据
Date(int year, int month, int day)
:_year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
ostream &operator<<(ostream &_cout, const Date& d)
{
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
istream &operator>>(istream &_cin, Date& d)
{
_cin >> d._year;
_cin >> d._month;
_cin >> d._day;
return _cin;
}
int main()
{
Date d(2018,1,1);
cin >> d;
cout << d << endl;
system("pause");
return 0;
}
注意:
1)友元函数的声明放在类的内外并无区别,它不受类的访问限定符限制,都说明是该类的友元函数,它的调用与一般函数相同。
2)一个函数可以是多个类的友元函数,只要在类内分别声明即可。
3)友元函数不是类的成员函数!!!
4)友元函数不能用const修饰。
2.友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类的非公有函数。
格式: friend class 类名;(类名是程序中已定义过的类)
注意:
1.友元关系是单向的,不具有交换性
例如:在B类中声明A类为其友元类,那么可以在A类中直接访问B的私有成员变量,但B无法访问A。
2.友元关系不能传递
例如: B是A的友元类,C是B的友元类,无法说明C是A的友元类。
// 以下两个类即为友元类
class Date; //前置声明
class Time
{
friend class Date; //声明为Date友元类,即Date类可直接访问Time类的私有成员
public:
Time(int hour=11,int minute=59,int second=59)
:_hour(hour)
, _minute(minute)
, _second(second)
{}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{}
void setTimeOfDate(int hour, int minute, int second)
{
//可以直接访问时间类的私有成员
_t._hour = hour;
_t._minute = minute;
_t._second = second;
}
void Print()
{
cout << _year << "年" << _month << "月" << _day << "日"<< _t._hour
<< "时" << _t._minute << "分" << _t._second << "秒"<<endl;
}
private:
int _year;
int _month;
int _day;
Time _t;
};
int main()
{
Date d(2019,3,4);
d.setTimeOfDate(16, 17, 18);
d.Print();
system("pause");
return 0;
}
3.内部类
3.1 概念
如果一个类定义在另一个类的内部,这个内部的类就叫内部类。
内部类也是一个独立的类,它并不属于外部类,所以不能通过外部类的对象去调用,外部类对内部类没有任何优先的访问权限。
注意: 这里的内部类是外部类的友元类,即内部类可以通过外部类的对象参数访问外部类中的成员。但是外部类不是内部类的友元类。
3.2 特性
1.内部类可以定义在外部类的public,protected,private都是可以的。
2.内部类可以直接访问外部类的static成员和枚举成员,不需要外部类的对象/类名。
3.sizeof(外部类)=外部类,与内部类无关。
//内部类
class A
{
private:
static int _k; //静态成员
int _h; //私有成员
public:
A()
{
_h = 10;
}
class B //B是A的内部类,B可直接访问A的成员变量
{
public:
void fun(const A&a)
{
cout << _k <<" "<< a._h << endl;//_k是静态成员可直接访问,_h是私有成员要通过对象的调用
}
};
};
int A::_k = 10; //静态成员必须在类外初始化
int main()
{
A::B b; // :: 访问限定符,表明B在A内
b.fun(A()); //未定义A类的对象,所以函数参数用A类构造一个对象
cout << sizeof(A) << endl; //4 static存在静态区,B是内部类不占空间,故sizeof(int _h)=4
cout << sizeof(A::B) << endl; //1 B是空类,故为1
system("pause");
return 0;
}