目录
一、友元是什么?
在C++中,友元(friend)是一种特殊的关系,通过将类A或函数A声明为类B的友元,它允许类A或函数A突破封装访问B类的私有成员。
所以友元是一种定义在类外部的普通函数(友元函数)或类(友元类),为了与该类的成员函数加以区别,它需要在类体内加以关键字friend进行说明。友元不是类的成员,但是它可以访问类中的私有成员。
二、为什么需要友元?
友元提供了一种突破封装的方式,让我们在没有访问器和修改器时也能对类的访问和修改,有时提供了便利。
如运算符重载,运算符重载通常是全局函数,而不是类的成员函数。因此,这些全局函数通常无法直接访问类的私有成员。在类中将该全局函数声明为类的友元,我们可以让它访问类的私有成员,从而实现正确的操作符重载。
以重载<<和>>运算符为例:
如果我们在类中重载<<和>>,使用起来会非常怪异:
因为在成员函数的第一个参数为隐藏的this指针,istream类的对象cin(ostream类的对象cout)则成为了第二个参数,所以使用时对象d在前,对象cin和cout在后。
想要正常使用需将>>和<<在全局域重载,这时我们要声明友元:
#include<iostream> using std::cin; using std::cout; using std::endl; using std::istream; using std::ostream; class Date { //声明友元 friend istream& operator>> (istream& _cin, Date& d); friend ostream& operator<< (ostream& _cout, Date& d); private: int _year; int _month; int _day; public: Date(int y = 2024, int m = 1, int d = 1) :_year(2024),_month(1),_day(1) { _year = y, _month = m, _day = d; } }; //重载写在需要的类的后面,不然找不到该类 //cin为标准库中istream类的对象,cout为ostream类的对象 istream& operator>> (istream& _cin, Date& d){ _cin >> d._year >> d._month >> d._day; return _cin;//有返回值才支持连续赋值 } ostream& operator<< (ostream& _cout, Date& d){ _cout << "当前日期为:" << d._year << "年" << d._month << "月" << d._day << "日\n"; return _cout; } int main(){ Date d; cout << "请输入年月日(使用空格分隔):"; cin >> d; cout << d; return 0; }
之所以能直接使用对象cout和cin输入输出所有内置类型,是因为在标准库中已经将它们的类的<<和>>都重载好了,<<和>>能够自动识别类型,是因为它们之间构成了函数重载。如果需要定制一个类的输入输出,在全局域重载<<和>>即可。
三、怎么使用友元?
在C++中使用关键字
friend
来声明友元关系。声明友元函数是friend加函数声明,声明友元类则是friend加class加类名:
当一个类或函数被声明为另一个类的友元时,它在外部也能够访问该类的私有成员。
友元会增加耦合度,破坏封装,可能会导致代码的可维护性和可读性降低,所以友元不宜多用。
四、友元的特性
- 一个函数可以是多个类的友元。
- 友元函数可访问类的私有和保护成员,但不是类的成员函数。
- 友元函数可以在类中的任何地方声明,不受类访问限定符限制。
- 友元函数不能用const修饰,编译器不会给友元函数传递this指针,因为友元函数不是类的成员函数。
- 友元关系不能传递。(如果B是A的友元,C是B的友元,则不能说明C是A的友元。)
- 友元类中的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
- 友元是单向的,不具有交换性。(比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。)
- 友元关系不能继承。
五、内部类
1. 内部类是什么?
内部类和外部类是相对的,如果类A定义在类B的内部,那么类A就是类B的内部类,类B是类A的外部类。
2. 内部类的作用
与友元类相比,内部类的存在可以提供更好的封装性和代码组织性。它允许外部类和内部类之间共享数据和方法,同时保持了较高的封装性。内部类可以访问外部类的私有成员,这使得内部类可以更方便地访问和操作外部类的数据。
3. 定义内部类
class A{
private:
int a;
public:
class B // B天生就是A的友元
{
private:
int b;
};
};
4. 特性
- 内部类就是外部类的友元类,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。
- 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名,因为内部类也是外部类的成员。
内部类中默认没有外部类的对象,无法访问外部类中非static成员(编译器不会像成员函数那样给内部类传递this指针)。所以内部类的成员函数如果要访问外部类中的非静态成员,需要给该成员函数传递一个外部类的对象作为参数。当然外部类要访问内部类的非静态public成员也需要一个内部类的对象。
- sizeof(外部类)=外部类,外部类的大小和内部类没有任何关系。
【总结】
------------------------END-------------------------
才疏学浅,谬误难免,欢迎各位批评指正。