3.8  友元:友元函数和友元类

友元函数 :既可以是不属于任何类的非成员函数,也可以是另一个类的成员函数,统称为友元函数。友元函数不是当前类的成员函数,而是独立于类的外部函数,但它可以访问该类所有的成员,包括私有成员、保护成员和公有成员。在类中声明友元函数时,需在其函数名前加上关键字friend,此声明可以放在公有部分、也可以放在保护和私有部分。友元函数可以定义在类部,也可以定义在类的外部。

3.8.1 将非成员函数声明为友元函数

  1. //1、将非成员函数声明为友元函数
  2. // 例3.33 友元函数的使用
  3. #include<iostream>
  4. using namespace std;
  5. class Gril{
  6. public:
  7. Gril(char* n,int a)
  8. {
  9. name = new char[strlen(n)+];
  10. strcpy(name,n);
  11. age = a;
  12. }
  13. ~Gril()
  14. {
  15. delete []name;
  16. }
  17. friend void display(Gril &);//声明友元函数 //friend void display(Gril );
  18. private:
  19. char* name;
  20. int age;
  21. };
  22. void display(Gril &x) //形参是对象的引用 //void display(Gril x) //形参是对象
  23. {
  24. cout<<"女孩的姓名是:"<<x.name<<","<<"年龄:"<<x.age<<endl;
  25. }
  26. int main()
  27. {
  28. Gril g("小丽",);
  29. display(g); //调用友元函数,实参是对象的引用
  30.  
  31. return ;
  32. }
  33.  
  34. /*
  35. 说明:1、友元函数虽然可以访问类对象的私有成员,但它毕竟不是成员函数,因此,在类的
  36. 外部定义友元函数时,不必像成员函数那样,在函数名前加 "类名::"
  37. 2、因为友元函数不是类的成员,所以它不能直接访问对象的数据成员,也不能通过this
  38. 指针访问对象的数据成员,它必须通过作为入口参数传递进来的对象名(或对象指针、对象引用)
  39. 来访问引用对象的数据成员。
  40. 3、由于函数display是Gril类的友元函数,所以display函数可以访问Gril中私有数据成员
  41. name、age。但是,在它们之前必须加上 "对象名."
  42. */

例1:非成员友元函数

  1. /*
  2. 需求:例如有两个类Gril和Boy,现要求打印所有的男生和女生的名字和年龄,我们只需一个
  3. 独立的函数print就能完成,但它必须同时定义为这两个类的友元函数。
  4. */
  5. //例如3.34 一个函数定义同时定义为两个类的友元函数
  6. #include<iostream>
  7. using namespace std;
  8. class Boy; //对Boy类的提前引用声明
  9. class Gril{
  10. public:
  11. Gril(char N[],int A)
  12. {
  13. strcpy(name,N);
  14. age = A;
  15. }
  16. friend void print(Gril &x) //声明print函数是Gril类的友元函数
  17. {
  18. cout<<"女孩的姓名是:"<<x.name<<" "<<"年龄:"<<x.age<<endl;
  19. }
  20. private:
  21. char name[];
  22. int age;
  23. };
  24. class Boy{ //声明Boy类
  25. public:
  26. Boy(char N[],int A)
  27. {
  28. strcpy(name,N);
  29. age = A;
  30. }
  31. friend void print(Boy &y) //声明print函数是Boy类的友元函数
  32. {
  33. cout<<"男孩的姓名是:"<<y.name<<" "<<"年龄:"<<y.age<<endl;
  34. }
  35. private:
  36. char name[];
  37. int age;
  38. };
  39. int main()
  40. {
  41. Gril g1("王萌",); //定义Gril类对象g1
  42. Gril g2("李芳",); //定义Gril类对象g2
  43. Gril g3("张丽",); //定义Gril类对象g3
  44.  
  45. Boy b1("张三",); //定义Boy类对象b1
  46. Boy b2("李四",); //定义Boy类对象b2
  47. Boy b3("王武",); //定义Boy类对象b3
  48.  
  49. print(g1); //调用友元函数,实参是Gril对象g1
  50. print(g2); //调用友元函数,实参是Gril对象g2
  51. print(g3); //调用友元函数,实参是Gril对象g3
  52.  
  53. print(b1); //调用友元函数,实参是Boy对象b1
  54. print(b2); //调用友元函数,实参是Boy对象b2
  55. print(b3); //调用友元函数,实参是Boy对象b3
  56.  
  57. return ;
  58. }

例2:非成员友元函数

  1. #include<iostream>
  2. using namespace std;
  3. class Boy; //对Boy类的提前引用声明
  4. class Gril{
  5. public:
  6. Gril(char N[],int A)
  7. {
  8. strcpy(name,N);
  9. age = A;
  10. }
  11. friend void print(Gril &,Boy &); //声明print函数是Gril类的友元函数
  12. private:
  13. char name[];
  14. int age;
  15. };
  16. class Boy{ //声明Boy类
  17. public:
  18. Boy(char N[],int A)
  19. {
  20. strcpy(name,N);
  21. age = A;
  22. }
  23. friend void print(Gril &,Boy &); //声明print函数是Boy类的友元函数
  24. private:
  25. char name[];
  26. int age;
  27. };
  28. void print(Gril &x,Boy &y) //定义print有元函数
  29. {
  30. cout<<"女孩的姓名是:"<<x.name<<" "<<"年龄:"<<x.age<<endl;
  31. cout<<"男孩的姓名是:"<<y.name<<" "<<"年龄:"<<y.age<<endl;
  32. }
  33. int main()
  34. {
  35. Gril g1("王萌",); //定义Gril类对象g1
  36. Gril g2("李芳",); //定义Gril类对象g2
  37. Gril g3("张丽",); //定义Gril类对象g3
  38.  
  39. Boy b1("张三",); //定义Boy类对象b1
  40. Boy b2("李四",); //定义Boy类对象b2
  41. Boy b3("王武",); //定义Boy类对象b3
  42.  
  43. print(g1,b1); //调用友元函数,实参是Gril对象g1,Boy对象b1
  44. print(g2,b2); //调用友元函数,实参是Gril对象g2,Boy对象b2
  45. print(g3,b3); //调用友元函数,实参是Gril对象g3,Boy对象b3
  46.  
  47. return ;
  48. }

3.8.2将成员函数声明为友元函数
除了一般的非成员函数可以作为某个类的友元外,一个类的成员函数也可以作为另一个类的友元,它是友元函数中的一种,成为友元成员函数。友元成员函数不仅可以访问自己所在类对象中的私有成员和公有成员,还可以访问friend声明语句所在类对象中的所有成员。

例3.35 一个类的成员函数作为另一个类的友元函数

  1. #include<iostream>
  2. #include<string>
  3. using namespace std;
  4. class Gril; //对类Gril提前引用声明
  5. class Boy{
  6. public:
  7. Boy(char* n,int a)
  8. {
  9. name = new char[strlen(n)+];
  10. strcpy(name,n);
  11. age = a;
  12. }
  13. void disp(Gril ); //声明函数dis为类Boy为成员函数
  14. ~Boy()
  15. {
  16. delete []name;
  17. }
  18. private:
  19. char* name;
  20. int age;
  21. };
  22. class Gril{
  23. public:
  24. Gril(char* n,int a)
  25. {
  26. name = new char[strlen(n)+];
  27. strcpy(name,n);
  28. age = a;
  29. }
  30. friend void Boy::disp(Gril ); //声明类Boy成员函数dis为类Gril的友元函数
  31. ~Gril()
  32. {
  33. delete []name;
  34. }
  35. private:
  36. char* name;
  37. int age;
  38. };
  39. void Boy::disp(Gril x) //定义类Boy的成员函数disp,同时也为类Gril的友元函数,
  40. { //形参为Gril类对象
  41. cout<<"男孩的姓名:"<<name<<endl; //函数disp作为Boy类的成员函数 ,可以访问Boy类对象的私有成员
  42. cout<<"男孩的年龄:"<<age<<endl; //注释同上
  43. cout<<"女孩的姓名:"<<x.name<<endl;//函数disp作为Gril类的友元函数,可以访问Gril类对象的私有成员
  44. cout<<"女孩的年龄:"<<x.age<<endl; //注释同上
  45. }
  46. int main()
  47. {
  48. Boy b("陈大林",);
  49. Gril g("张晓好",);
  50. b.disp(g);//调用Boy类的对象b的成员函数和Gril类的友元函数disp,实参是Gril类的对象g
  51. //因为函数disp是Boy类的成员函数,所以无需通过传递对象,可以直接访问自己的私有数据成员
  52. return ;
  53. }

3.8.3 友元类

不仅函数可以作为一个类的友元,一个类也可以作为另一个类的友元,称为友元类。友元类
的说明方法是在另一个类声明中加入语句。
friend class 类名;

此类名是友元类的类名。这条语句可以放在公有部分,也可以放在私有部分。例如,

class Y{
      ...
};
class X{
      ...
friend class Y; //声明类Y是类X的友元类
      ...
};

当类Y被说明类X的友元时,类Y的所有成员函数都成为类X的友元函数,这就意味着作为
友元类Y中的所有成员函数都可以访问类X中的所有成员(包括私有成员)。

下面的例子中,声明了两个类Boy和Gril,类Boy声明为类Gril的友元,因此类Boy的成员
函数都能成为类Gril的友元函数,它们都可以访问类Gril的私有成员。

例 3.36 友元类的应用

  1. #include<iostream>
  2. #include<string>
  3. using namespace std;
  4. class Gril; //对友元类的提前引用声明
  5. class Boy{
  6. public:
  7. Boy(char* n,int a)
  8. {
  9. name=new char[strlen(n)+];
  10. strcpy(name,n);
  11. age = a;
  12. }
  13. ~Boy()
  14. {
  15. delete []name;
  16. }
  17. void display1(Gril &); //声明函数display1为类Boy的成员函数
  18. void display2(Gril &); //声明函数display2为类Boy的成员函数
  19. private:
  20. char* name;
  21. int age;
  22. };
  23. class Gril{
  24. public:
  25. Gril(char* n,int a)
  26. {
  27. name=new char[strlen(n)+];
  28. strcpy(name,n);
  29. age = a;
  30. }
  31. ~Gril()
  32. {
  33. delete []name;
  34. }
  35.  
  36. friend class Boy; //声明Boy为类Gril的友元类,则类Boy中的所有成员函数为Gril类的友元成员函数
  37.  
  38. private:
  39. char* name;
  40. int age;
  41. };
  42. void Boy::display1(Gril &x)
  43. {
  44. cout<<"男孩的姓名是:"<<name<<endl;
  45. cout<<"女孩的姓名是:"<<x.name<<endl;
  46. }
  47.  
  48. void Boy::display2(Gril &x)
  49. {
  50. cout<<"男孩的年龄是:"<<age<<endl;
  51. cout<<"女孩的年龄是:"<<x.age<<endl;
  52. }
  53. int main()
  54. {
  55. Boy b("陈大林",);
  56. Gril g("张晓好",);
  57.  
  58. b.display1(g);
  59. b.display2(g);
  60.  
  61. return ;
  62. }

注意:声明一个类A为另一个类B的友元类(则类A的所有成员函数都是类B的友元函数,友元类A的所有成员函数既可以访问自己本类的所有成员,也可以访问类B的所有成员)

  1. #include<iostream>
  2. #include<string>
  3. using namespace std;
  4. class Gril; //对友元类的提前引用声明
  5. class Boy{
  6. public:
  7. Boy(char* n,int a)
  8. {
  9. name=new char[strlen(n)+];
  10. strcpy(name,n);
  11. age = a;
  12. }
  13. ~Boy()
  14. {
  15. delete []name;
  16. }
  17. void display(Gril &); //声明函数display为类Boy的成员函数
  18. private:
  19. char* name;
  20. int age;
  21. };
  22. class Gril{
  23. public:
  24. Gril(char* n,int a)
  25. {
  26. name=new char[strlen(n)+];
  27. strcpy(name,n);
  28. age = a;
  29. }
  30. ~Gril()
  31. {
  32. delete []name;
  33. }
  34.  
  35. friend class Boy; //声明Boy为类Gril的友元类,则类Boy中的所有成员函数为Gril类的友元成员函数
  36.  
  37. private:
  38. char* name;
  39. int age;
  40. };
  41. void Boy::display(Gril &x)
  42. {
  43. cout<<"男孩的姓名是:"<<name<<endl;
  44. cout<<"男孩的年龄是:"<<age<<endl;
  45. cout<<"女孩的姓名是:"<<x.name<<endl;
  46. cout<<"女孩的年龄是:"<<x.age<<endl;
  47. }
  48. int main()
  49. {
  50. Boy b("陈大林",);
  51. Gril g("张晓好",);
  52.  
  53. b.display(g);
  54.  
  55. return ;
  56. }
  57.  
  58. /*
  59. 说明:友元关系是单向的,不具有交换性。若声明了类X是类Y的友元类(即在类Y定义中声明X为friend类),
  60. 不等于类Y一定是X的友元,这就要看在类X中是否有相应的声明。
  61.  
  62. 友元关系也不具有传递性,若类X是类Y的友元,类Y是类Z的友元,不一定类X是类Z的友元。如果想让类X
  63. 是类Z的友元类,应在类Z中作出声明。
  64. */

C++:友元(非成员友元函数、成员友元函数、友元类)的更多相关文章

  1. C/C++杂记:深入理解数据成员指针、函数成员指针

    1. 数据成员指针 对于普通指针变量来说,其值是它所指向的地址,0表示空指针. 而对于数据成员指针变量来说,其值是数据成员所在地址相对于对象起始地址的偏移值,空指针用-1表示.例: 代码示例: str ...

  2. 【转载】C/C++杂记:深入理解数据成员指针、函数成员指针

    原文:C/C++杂记:深入理解数据成员指针.函数成员指针 1. 数据成员指针 对于普通指针变量来说,其值是它所指向的地址,0表示空指针.而对于数据成员指针变量来说,其值是数据成员所在地址相对于对象起始 ...

  3. C++运算符重载三种形式(成员函数,友元函数,普通函数)详解

    首先,介绍三种重载方式: //作为成员函数重载(常见) class Person{ Private: string name; int age; public: Person(const char* ...

  4. About The Order of The Declarations And Definition When Making a Member Function a Friend.关于使类成员成为另一个类友元函数的声明顺序和定义。

    If only member function clear of WindowMgr is a friend of Screen, there are some points need to note ...

  5. C++走向远洋——34(友元函数,成员函数和一般函数的区别)

    */ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:youyuan.cpp * 作者:常轩 * 微信公众号:Worl ...

  6. C++基础-4-封装(构造函数与析构函数,深拷贝与浅拷贝,静态成员,this,友元,const修饰成员函数)

    4. 封装 4.1.1 封装的意义 1 #include<iostream> 2 #include<string> 3 using namespace std; 4 5 con ...

  7. c++全局函数 && 成员函数

    #include<iostream> using namespace std; class Test { public: Test(, ) { this->a = a; this-& ...

  8. cc30a_demo-CppPrimer_友元与继承-txwtech友元关系不能继承-要明确授予友元

    //友元可以访问类的private与protected成员//友元关系不能继承-要明确授予友元 #include <iostream>//CppPrimer_友元与继承-txwtech-- ...

  9. C++_static与非static成员(函数)

    static与非static成员(函数)  <C++ Primer>第4版399页: 对于特定类类型的全体对象而言,访问一个全局变量有时是必要的.然而,全局变量会破坏封装:对象需要支持特定 ...

  10. 类成员函数作为pthread_create函数参数

    from:http://www.cnblogs.com/shijingxiang/articles/5389294.html 近日需要将线程池封装成C++类,类名为Threadpool.在类的成员函数 ...

随机推荐

  1. 图解 CSS: 理解样式表的逻辑(转载)

    原文:http://www.cnblogs.com/del/archive/2009/02/01/1382141.html 样式表可以是外部的.内联的或嵌入的; 链接外部样式文件一般是:<lin ...

  2. C++实现数字媒体二维图像变换

    C++实现数字媒体二维图像变换 必备环境 glut.h 头文件 glut32.lib 对象文件库 glut32.dll 动态连接库 程序说明 C++实现了用glut画正方形,画三角形的功能.并附带放大 ...

  3. virtualbox usb连接问题解决

    生命在于折腾... 神奇的liinux... ubuntu 14.04 LTS sudo apt-get install virtualbox -y 然后建好虚拟机之后(windows也好,linux ...

  4. 微软职位内部推荐-SDE2 (Windows - Audio)

    微软近期Open的职位: SDE2 (Windows - Audio) Windows Partner Enablement team in Operating System Group is loo ...

  5. c#之委托总结

    1.委托的声明和编译原理 声明委托: delegate void Translate(string str); 通过反编译之后可得下面代码 private sealed class Translate ...

  6. Careercup - Facebook面试题 - 4713484755402752

    2014-05-02 00:30 题目链接 原题: Given two arrays of sorted integers, merge them keeping in mind that there ...

  7. 在ASP.NET MVC应用程序中实现Server.Transfer()类似的功能

    在ASP.NET MVC应用程序中,如果使用Server.Transfer()方法希望将请求转发到其它路径或者Http处理程序进行处理,都会引发“为xxx执行子请求时出错”的HttpException ...

  8. 消息传递选择:返回值 or 抛出异常

    1.返回值 bool 不应该用来表示函数是否调用成功,而应该返回业务值,例如 IsExist,HasNext

  9. 在一个字符串(1<=字符串长度<=10000,全部由大小写字母组成)中找到第一个只出现一次的字符,并返回它的位置

    // test20.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<iostream> #include< ...

  10. c++中new和delete的使用方法

    c++中new和delete的使用方法 new和delete运算符用于动态分配和撤销内存的运算符 new用法: 1.     开辟单变量地址空间 1)new int;  //开辟一个存放数组的存储空间 ...