目录
友元
概述: 允许一个类或函数访问另一个类的私有成员,即使它不是该类的成员函数。友元关系的主要目的是提供灵活性,允许外部函数或类访问类的私有部分,而不违反封装性原则。
友元的关键字为 friend
一、函数做友元
概述:在当前类以外定义的、不属于当前类的函数也可以在类中声明,但要在前面加 friend 关键字,这样就构成了友元函数。友元函数可以是不属于任何类的非成员函数,也可以是其他类的成员函数。友元函数可以访问当前类中的所有成员,包括 public、protected、private 属性的。
1.1全局函数做友元
概述:全局函数做友元可以不在类内访问类的成员
语法:把全局函数的声明加上 friend 关键字放在类中 friend void show(Student* pstu);
实例
#include <iostream>
using namespace std;
class Student
{
public:
Student(string name, int age, float score);
friend void show(Student* pstu); // 将全局函数show()声明为友元函数
private:
string m_name;
int m_age;
float m_score;
};
// 构造函数
Student::Student(string name, int age, float score) : m_name(name), m_age(age), m_score(score) { }
// 全局函数
void show(Student* pstu)
{
cout << pstu->m_name << "的年龄是 " << pstu->m_age << ",成绩是 " << pstu->m_score << endl;
}
int main()
{
Student stu("小明", 15, 90.6);
show(&stu); // 调用友元函数
// 使用 new 运算符动态分配了一个 Student 对象,然后将指向该对象的指针存储在 pstu 变量中
Student* pstu = new Student("李磊", 16, 80.5);
show(pstu); // 调用友元函数
delete pstu; // 释放动态分配的内存
return 0;
}
结果
小明的年龄是 15,成绩是 90.6
李磊的年龄是 16,成绩是 80.5
show() 是一个全局函数,它不属于任何类,它的作用是输出学生的信息。而m_name、m_age、m_score 是 Student 类的 private 成员,原则上不能通过对象访问,将 show() 声明为 Student 类的友元函数后,就可以使用这些 private 成员了。
注意,友元函数不同于类的成员函数,在友元函数中不能直接访问类的成员,必须要借助对象。
void show() // 这里函数没有传递参数 所以是错误的
{
cout<<m_name<<"的年龄是 "<<m_age<<",成绩是 "<<m_score<<endl;
}
成员函数在调用时会隐式地增加 this 指针,指向调用它的对象,从而使用该对象的成员;而 show() 是非成员函数,没有 this 指针,编译器不知道使用哪个对象的成员,所以必须通过参数传递对象(可以直接传递对象,也可以传递对象指针或对象引用),并在访问成员时指明对象。
1.2成员函数做友元
概述:可以使一个类的成员函数访问另一个类中的所有变量
语法:把要做友元的成员函数在另一个类中加 friend关键字声明
friend void Student::show(Address* addr); 注意带作用域
实例
#include <iostream>
using namespace std;
class Address; // 提前声明Address类
// 声明Student类
class Student
{
public:
// 有参构造
Student(string name, int age, float score);
// 成员函数的定义
void show(Address* addr);
private:
string m_name;
int m_age;
float m_score;
};
// 声明Address类
class Address
{
public:
// 有参构造
Address(string province, string city, string district);
// 将Student类中的成员函数show()声明为Address的友元函数
friend void Student::show(Address* addr);
private:
string m_province; // 省份
string m_city; // 城市
string m_district; // 区(市区)
};
// 实现Address类
Address::Address(string province, string city, string district)
{
m_province = province;
m_city = city;
m_district = district;
}
// Student类 初始化列表 构造函数的实现
Student::Student(string name, int age, float score) : m_name(name), m_age(age), m_score(score) { }
// Student类 成员函数 show(Address* addr)的实现
void Student::show(Address* addr)
{
cout << this->m_name << "的年龄是 " << this->m_age << ",成绩是 " << this->m_score << endl;
cout << "家庭住址:" << addr->m_province << "省" << addr->m_city << "市" << addr->m_district << "区" << endl;
}
int main()
{
Student stu("张三", 16, 95.5f); // 创建Student 对象并初始化
Address addr("湖南", "长沙", "岳麓区"); // 创建Address 对象并初始化
stu.show(&addr); // stu对象调用成员函数 因为成员函数是Address的友元 所以可访问Address类 的成员
// 使用 new 运算符动态分配了一个 Student 对象,然后将指向该对象的指针存储在 pstu 变量中 指向类的指针
Student* pstu = new Student("李四", 16, 80.5);
// 使用 new 运算符动态分配了一个 Address 对象,然后将指向该对象的指针存储在 paddr 变量中
Address* paddr = new Address("湖南", "长沙", "雨花区");
pstu->show(paddr); // 解引用指针调用成员函数show 因为show是Address的友元 所以把Address的对象传进去可以访问Address 的成员
delete pstu; // 释放动态分配的内存 防止内存泄漏
delete paddr; // 释放动态分配的内存 防止内存泄漏
return 0;
}
结果
张三的年龄是 16,成绩是 95.5
家庭住址:湖南省长沙市岳麓区区
李四的年龄是 16,成绩是 80.5
家庭住址:湖南省长沙市雨花区区
注意:
-
提前声明Address 类:因为在Student 类中的 void show(Address* addr); 函数是Address 类的友元函数,Student 类的声明在Address 类的前面,而Student类使用用到了 Address 类 ,所以要在Student类 前面(第三行已经声明)声明Address 类。
-
Address 类中的show() 函数的实现要放在Address 类的声明后面:因为show() 函数体中用到了 Address 的成员 province、city、district,如果提前不知道 Address 的具体声明内容,就不能确定 Address 是否拥有该成员(类的声明中指明了类有哪些成员)
-
类的提前声明。一般情况下,类必须在正式声明之后才能使用;但是某些情况下(如上例所示),只要做好提前声明,也可以先使用。
-
类的提前声明的使用范围是有限的,只有在正式声明一个类以后才能用它去创建对象,因为创建对象时要为对象分配内存,在正式声明类之前,编译器无法确定应该为对象分配多大的内存。编译器只有在“见到”类的正式声明后(其实是见到成员变量),才能确定应该为对象预留多大的内存。
-
一个函数可以被多个类声明为友元函数,这样就可以访问多个类中的 private 成员。
二、类做友元
概述:将整个类声明为另一个类的这就是友元类。友元类中的所有成员函数都是另外一个类的友元函数。例如将类 B 声明为类 A 的友元类,那么类 B 中的所有成员函数都是类 A 的友元函数,可以访问类 A 的所有成员,包括 public、protected、private 属性的。
语法:把要做友元的类加关键字 friend 声明放在另一个类中 friend class goodGay;
实例
#include <iostream>
using namespace std;
class Building; // 提前声明 Building 类
class goodGay
{
public:
goodGay(); // 构造函数
void visit(Building* buiding);
};
class Building
{
//告诉编译器 goodGay类是Building类的好朋友,可以访问到Building类中私有内容
friend class goodGay;
public:
Building(); // 无参构造
string m_SittingRoom; // 客厅
private:
string m_BedRoom; // 卧室
};
// Building 构造函数的实现 给成员变量赋初始值
Building::Building()
{
this->m_SittingRoom = "客厅";
this->m_BedRoom = "卧室";
}
// goodGay 构造函数的实现
goodGay::goodGay()
{
/*building = new Building;*/
}
// goodGay 成员函数的实现
void goodGay::visit(Building* buiding)
{
// 因为goodGay 是Building 的友元 所以可以访问
cout << "好朋友正在访问" << buiding->m_SittingRoom << endl;
cout << "好朋友正在访问" << buiding->m_BedRoom << endl;
}
void test01()
{
goodGay gg;
Building buiding;
gg.visit(&buiding);
}
int main()
{
test01();
return 0;
}
结果
好朋友正在访问客厅
好朋友正在访问卧室
注意:
-
友元的关系是单向的而不是双向的。如果声明了类 B 是类 A 的友元类,不等于类 A 是类 B 的友元类,类 A 中的成员函数不能访问类 B 中的 private 成员。
-
友元的关系不能传递。如果类 B 是类 A 的友元类,类 C 是类 B 的友元类,不等于类 C 是类 A 的友元类。
-
除非有必要,一般不建议把整个类声明为友元类,而只将某些成员函数声明为友元函数,这样更安全一些。
总结:借助友元(friend),可以使得其他类中的成员函数以及全局范围内的函数访问当前类的成员包含private 权限的成员。
三、前后文跳转链接
上一篇:C++ this指针详解https://blog.csdn.net/qq_61692089/article/details/134159161?spm=1001.2014.3001.5501
下一篇:C++ 继承详解https://blog.csdn.net/qq_61692089/article/details/134217341?spm=1001.2014.3001.5501