【C++】C++学习之——friend 友元和内部类

[本节内容]

友元函数
友元类
内部类

注意:友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。

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;
}

猜你喜欢

转载自blog.csdn.net/ly_6699/article/details/88079162