我们知道类的私有成员只能在类的成员函数内部访问,如果想在别处访问对象的私有成员,只能通过类提供的接口(成员函数)间接地进行。这固然能够带来数据隐藏的好处,利于将来程序的扩充,但也会增加程序书写的麻烦。

C++ 设计者认为, 如果有的程序员真的非常怕麻烦,就是想在类的成员函数外部直接访问对象的私有成员,那还是做一点妥协以满足他们的愿望为好,这也算是眼前利益和长远利益的折中。因此,C++ 就有了友元(friend)的概念。打个比方,这相当于是说:朋友是值得信任的,所以可以对他们公开一些自己的隐私。

友元分为两种:友元函数和友元类。

1. 友元函数

类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。

在定义一个类的时候,可以把一些函数(包括全局函数和其他类的成员函数)声明为“友元”,这样那些函数就成为该类的友元函数,在友元函数内部就可以访问该类对象的私有成员了。

将全局函数声明为友元的写法如下:

friend  返回值类型  函数名(参数表);

将其他类的成员函数声明为友元的写法如下:

friend  返回值类型  其他类的类名::成员函数名(参数表);

但是,不能把其他类的私有成员函数声明为友元函数。

示例代码如下:

#include <iostream>
using namespace std;class Student
{public:Student(string i, string n, int a); // 简单的构造函数~Student(); // 析构函数string getID();friend int getAge(Student stu); // 友元函数private:string id;string name;int age;
};Student::Student(string i="", string n="", int a=0)
{cout << "constructor func run" << endl;id = i;name = n;age = a;
}Student::~Student()
{cout << "destructor func run" << endl;
}string Student::getID()
{return id;
}// getAge() 不是任何类的成员函数
int getAge(Student stu)
{//  因为 getAge() 是 Student 的友元函数,所以它可以直接访问该类的任何成员 */return stu.age;
}int main()
{Student stu ("0001", "Jack", 18);// 使用友元函数获取年龄int age = getAge(stu);cout << "age is " << age << endl;return 0;
}

输出结果:

constructor func run
destructor func run
age is 18
destructor func run

因为友元函数没有 this指针,则参数要有三种情况:

  • 要访问非 static成员时,需要对象做参数;
  • 要访问 static成员或全局变量时,则不需要对象做参数;
  • 如果做参数的对象是全局对象,则不需要对象做参数;
  • 可以直接调用友元函数,不需要通过对象或指针;

示例代码:

class INTEGER
{friend void Print(const INTEGER& obj);//声明友元函数
};void Print(const INTEGER& obj)
{//函数体
}void main()
{INTEGER obj;Print(obj);//直接调用
}

2. 友元类

友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。

一个类 A 可以将另一个类 B 声明为自己的友元,类 B 的所有成员函数就都可以访问类 A 对象的私有成员。在类定义中声明友元类的写法如下:

friend  class  类名;

示例代码:

#include <iostream>
using namespace std;class Student
{public:Student(string i, string n, int a); // 简单的构造函数string getID();friend class Teacher; // 友元函数private:string id;string name;int age;
};Student::Student(string i="", string n="", int a=0)
{cout << "constructor func run" << endl;id = i;name = n;age = a;
}class Teacher
{public:void checkStudent(Student stu);
};void Teacher::checkStudent(Student stu)
{// Teacher 是 Student 的友元类,它可以直接访问 Student 类的任何成员cout << "sut.age is " << stu.age << endl;cout << "sut.name is " << stu.name << endl;
}int main()
{Student stu ("0001", "Jack", 18);Teacher t;t.checkStudent(stu);return 0;
}

输出结果:

constructor func run
sut.age is 18
sut.name is Jack

友元关系在类之间不能传递,即类 A 是类 B 的友元,类 B 是类 C 的友元,并不能导出类 A 是类 C 的友元。“咱俩是朋友,所以你的朋友就是我的朋友”这句话在 C++ 的友元关系上不成立,当然在现实生活中很大概率也是不能成立的。

我们使用了友元之后,发现在设计程序的时候方便了很多。原先的那些私有成员都能轻松地被访问了。于是我们不用去写那些繁琐的成员函数,程序执行的时候也减少了函数的调用次数,提高了运行效率。

友元的存在,破坏了类的封装性。一个类出现问题,就不仅仅是由这个类本身负责了,还可能和它众多的友元有关。这无疑使得检查调试的范围突然扩大了许多,难度也陡然增加。

所以,我们在使用友元的时候,权衡使用友元的利弊,使程序达到最佳的效果。

C++ 笔记(30)— 友元函数与友元类相关推荐

  1. C++学习笔记(10)运算符重载,友元函数,友元类

    c++允许我们为运算符定义专门的函数,这被称为运算符重载: 运算符可以简化字符串的操作,'+',以及使用关系运算符比较字符串,[ ]运算符访问向量中的元素: 例如: #include <iost ...

  2. c++远征之模板篇——友元函数、友元类

    以下内容源于慕课网的学习整理,如有侵权,请告知删除. 一.友元函数 1.分类 全局函数-->友元全局函数 成员函数-->友元成员函数 2.关键字friend 友元全局函数 如下,利用fri ...

  3. C++友元函数、友元类与类模板

    文章目录 一.普通函数声明为友元涵数 二.声明类的成员函数为其他类的友元函数 三.类模板的使用 一.普通函数声明为友元涵数 #include <iostream> using namesp ...

  4. Th3.9:友元函数、友元类、友元成员函数详述

     本博客将记录:类的相关知识点的第9节的笔记! (这个在学习C++基础课程时已经学习过一次了,这里再次简单地回顾一下而已) 今天总结的知识分为以下3个点:   一.友元函数(对于非成员函数而言)   ...

  5. C++友元函数和友元类(C++ friend)详解

    在看VISP视觉库的时候遇到友元函数: Friends void swap (vpDetectorAprilTag &o1, vpDetectorAprilTag &o2) 在定义一个 ...

  6. 模板类中使用友元函数的方式,派生类友元函数对基类的成员使用情况

    在一般友元函数的前面加上 template<typename T),注意在函数的声明和定义处都要加这个模板 例如: //模板类,长方体类 template <typename Elemen ...

  7. c++友元函数与友元类

    友元函数和友元类的需要: 类具有封装和信息隐藏的特性.只有类的成员函数才能访问类的私有成员,程序中的其他函数是无法访问私有成员的.非成员函数可以访问类中的公有成员,但是如果将数据成员都定义为公有的,这 ...

  8. C++友元函数和友元类(一)

    在 C++ 中,一个类中可以有 public.protected.private 三种属性的成员,通过对象可以访问 public 成员,只有本类中的函数可以访问本类的 private 成员.现在,我们 ...

  9. friend之友元函数和友元类

    // friend_func.cpp : Defines the entry point for the console application. 写一个函数求两个点的中点 //友元函数可以访问类的私 ...

最新文章

  1. css盒子模型、边框border、外边距margin、填充padding、轮廓outline
  2. 使用LINQ更新集合中的所有对象
  3. 用java雷电游戏_Java实现仿雷电游戏
  4. 学web前端一定要这样学,不然学完找不到工作哭都来不及!
  5. continue break
  6. 荣威i5能升级鸿蒙系统吗,荣威i5更新系统方法
  7. es中主分片和副本分片
  8. Orion Network Configuration Manager (NCM)测试报告
  9. 代码换行符_Excel办公技巧:如何定位、替换、清除单元格中的换行符?
  10. python隐式调用_python 的隐式指针特征与class inheritance
  11. Java文件上传数据库(并保存本地)、word转pdf并进行页面预览
  12. css 图片反色,颜色反色,高斯模糊
  13. 微信小程序下拉刷新真机没效果_微信小程序下拉刷新上拉加载的两种实现方法...
  14. 机电工程学院互联网+特色专业17级顶岗实习欢送会​圆满落幕
  15. u盘安装红旗linux操作系统,如何用u盘安装红旗linux?
  16. android6.0 power按键深入分析
  17. 苹果手机使用技巧篇:教你完美使用好苹果手机的4个方法
  18. bugku never give up
  19. 1分钟搞定ubuntu下配置mysql 奥利给
  20. 《 FreeSWITCH权威指南》——1.2 电话实现技术

热门文章

  1. GCC 同时编译多个 C/C++ 文件
  2. Python 标准库之 time
  3. API pytorch tensorflow
  4. 受用一生的高效 PyCharm 使用技巧(一)
  5. 命名实体识别学习笔记——使用Ltp
  6. 黎曼曲面Riemann Surface
  7. C++ multimap 的使用
  8. Android aar 代码查看
  9. CountDownTimer的简单使用
  10. DOS命令行操作MySQL常用命令