[本节内容]
友元函数中的友元类和内部类
注意:友元函数本质上是提供了一种突破封装的方式,有时会为我们带来更多的便利。但是友元函数的使用会增加耦合度,破坏类的封装性,所以友元不宜多用。
1.友元函数的背景
在学习友元函数之前,如果我们想要输入一个完整的对象,即使这个对象成员比较简单(例如只有一个成员a),或者对象成员本身构成一个整体(例如日期),目前我们只能通过 类.成员
访问再用 cout<<
输出对象的全部成员,从而达到预期。但如果我们想直接用cout<<
输出一个对象呢?
我们能不能完成呢?直接看下面这段代码:
class A
{
public:
A(int a = 10) //对象A的构造函数
{
_a = a;
}
private:
int _a;
};
int main()
{
A a;
cout << a ;
return 0;
}
大家运行后会发现编译不通过,因为 cout << a;
这里的a是一个对象,无法用<<输出。这时我们会想到尝试重载operator<<去输出这个对象,于是可以对代码进行修改:
class A
{
public:
A(int a = 10)
{
_a = a;
}
void operator<<(ostream &out) //重载<<
{
out << _a; //输出a的值
}
private:
int _a;
};
int main()
{
A a;
cout << a ;
return 0;
}
cout << a ;
运行后大家会发现,还是编译错误!!因为在使用重载的<<运算符时,我们将输出流对象 cout作为了运算符的第一个参数,但实际上成员函数规定函数中隐含的this指针(即左操作数)才应该是这个重载的运算符的第一个参数。此时两个参数同时在抢占第一个参数的位置,所以导致程序编译不通过。
这时,如果我们改为用a.operator<<(cout) ;或者 a << cout;
完成输出这个对象内容的效果是没问题的(如上图),但是这样使用<<与我们习惯中的用法相反。所以我们将operator<<重载为成员函数不是最好的办法。
那么如果你为了让this指针消失,想要把operator<< 重载成全局函数,那不用实践都知道是错误的,因为全局函数在类外无法访问类内的成员。那么此时还有一个解决办法,就是把这个类外函数operator<<
写成类的友元函数~~
具体实现的做法看下面的代码:
#include<iostream>
using namespace std;
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; // 规定<<的使用规则为:cout是第一个参数,a成员是第二个参数
return _cout;
}
int main()
{
A a;
cout<<a; //此时可正确输出a对象中成员_a的值
return 0;
}
由此可以看出,友元函数是一个能直接访问类的私有成员的非成员函数,简单说它是一个普通函数,定义在类外部所以也不属于任何类。使用时只需要在类的内部加上friend关键字进行声明,即可访问此类的所有成员。
格式: friend 类型 函数名 (形式参数);
注:友元函数中没有this指针,它的参数有以下情况:
a.要访问非静态成员时,需要对象做参数
b.要访问静态成员或全局变量时,直接成员做参数
下面我将演示如何使用cin>>和cout<<
输出一个日期类。
实现输出Date类,我们只需要在Date类中,声明operator>>和operator<<的重载函数为友元函数即可。
使用友元函数相关的操作如下,模拟完整的Date类代码单独在一篇博客中,有需要的自取 Date类的完整代码
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修饰。
5) 友元函数无this指针,必须通过对象名访问成员即 对象.成员
方式
2.友元类
友元类与友元函数不同。友元类中所有的成员函数都是另一个类的友元函数,都可以访问到另一个类的非公有成员/函数。
格式: friend class 类名;(类名是程序中已定义过的类)
注意:
1.友元关系是单向的,不具有交换性
例如:在B类中声明A类为其友元类,那么在A类中可以直接访问B的私有成员,但B无法访问A的成员。
2.友元关系不能传递
例如: B是A的友元类,C是B的友元类,无法说明C是A的友元类。
下面代码中Date和Time就是友元类:
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();
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;
}
以上就是我关于友元类和内部类学习的一些内容啦,后续会继续补充。喜欢小编的写作风格可以关注小编~