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

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

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

//1、将非成员函数声明为友元函数
// 例3.33 友元函数的使用
#include<iostream>
using namespace std;
class Gril{
public:
Gril(char* n,int a)
{
name = new char[strlen(n)+];
strcpy(name,n);
age = a;
}
~Gril()
{
delete []name;
}
friend void display(Gril &);//声明友元函数 //friend void display(Gril );
private:
char* name;
int age;
};
void display(Gril &x) //形参是对象的引用 //void display(Gril x) //形参是对象
{
cout<<"女孩的姓名是:"<<x.name<<","<<"年龄:"<<x.age<<endl;
}
int main()
{
Gril g("小丽",);
display(g); //调用友元函数,实参是对象的引用 return ;
} /*
说明:1、友元函数虽然可以访问类对象的私有成员,但它毕竟不是成员函数,因此,在类的
外部定义友元函数时,不必像成员函数那样,在函数名前加 "类名::"
2、因为友元函数不是类的成员,所以它不能直接访问对象的数据成员,也不能通过this
指针访问对象的数据成员,它必须通过作为入口参数传递进来的对象名(或对象指针、对象引用)
来访问引用对象的数据成员。
3、由于函数display是Gril类的友元函数,所以display函数可以访问Gril中私有数据成员
name、age。但是,在它们之前必须加上 "对象名."
*/

例1:非成员友元函数

/*
需求:例如有两个类Gril和Boy,现要求打印所有的男生和女生的名字和年龄,我们只需一个
独立的函数print就能完成,但它必须同时定义为这两个类的友元函数。
*/
//例如3.34 一个函数定义同时定义为两个类的友元函数
#include<iostream>
using namespace std;
class Boy; //对Boy类的提前引用声明
class Gril{
public:
Gril(char N[],int A)
{
strcpy(name,N);
age = A;
}
friend void print(Gril &x) //声明print函数是Gril类的友元函数
{
cout<<"女孩的姓名是:"<<x.name<<" "<<"年龄:"<<x.age<<endl;
}
private:
char name[];
int age;
};
class Boy{ //声明Boy类
public:
Boy(char N[],int A)
{
strcpy(name,N);
age = A;
}
friend void print(Boy &y) //声明print函数是Boy类的友元函数
{
cout<<"男孩的姓名是:"<<y.name<<" "<<"年龄:"<<y.age<<endl;
}
private:
char name[];
int age;
};
int main()
{
Gril g1("王萌",); //定义Gril类对象g1
Gril g2("李芳",); //定义Gril类对象g2
Gril g3("张丽",); //定义Gril类对象g3 Boy b1("张三",); //定义Boy类对象b1
Boy b2("李四",); //定义Boy类对象b2
Boy b3("王武",); //定义Boy类对象b3 print(g1); //调用友元函数,实参是Gril对象g1
print(g2); //调用友元函数,实参是Gril对象g2
print(g3); //调用友元函数,实参是Gril对象g3 print(b1); //调用友元函数,实参是Boy对象b1
print(b2); //调用友元函数,实参是Boy对象b2
print(b3); //调用友元函数,实参是Boy对象b3 return ;
}

例2:非成员友元函数

#include<iostream>
using namespace std;
class Boy; //对Boy类的提前引用声明
class Gril{
public:
Gril(char N[],int A)
{
strcpy(name,N);
age = A;
}
friend void print(Gril &,Boy &); //声明print函数是Gril类的友元函数
private:
char name[];
int age;
};
class Boy{ //声明Boy类
public:
Boy(char N[],int A)
{
strcpy(name,N);
age = A;
}
friend void print(Gril &,Boy &); //声明print函数是Boy类的友元函数
private:
char name[];
int age;
};
void print(Gril &x,Boy &y) //定义print有元函数
{
cout<<"女孩的姓名是:"<<x.name<<" "<<"年龄:"<<x.age<<endl;
cout<<"男孩的姓名是:"<<y.name<<" "<<"年龄:"<<y.age<<endl;
}
int main()
{
Gril g1("王萌",); //定义Gril类对象g1
Gril g2("李芳",); //定义Gril类对象g2
Gril g3("张丽",); //定义Gril类对象g3 Boy b1("张三",); //定义Boy类对象b1
Boy b2("李四",); //定义Boy类对象b2
Boy b3("王武",); //定义Boy类对象b3 print(g1,b1); //调用友元函数,实参是Gril对象g1,Boy对象b1
print(g2,b2); //调用友元函数,实参是Gril对象g2,Boy对象b2
print(g3,b3); //调用友元函数,实参是Gril对象g3,Boy对象b3 return ;
}

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

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

#include<iostream>
#include<string>
using namespace std;
class Gril; //对类Gril提前引用声明
class Boy{
public:
Boy(char* n,int a)
{
name = new char[strlen(n)+];
strcpy(name,n);
age = a;
}
void disp(Gril ); //声明函数dis为类Boy为成员函数
~Boy()
{
delete []name;
}
private:
char* name;
int age;
};
class Gril{
public:
Gril(char* n,int a)
{
name = new char[strlen(n)+];
strcpy(name,n);
age = a;
}
friend void Boy::disp(Gril ); //声明类Boy成员函数dis为类Gril的友元函数
~Gril()
{
delete []name;
}
private:
char* name;
int age;
};
void Boy::disp(Gril x) //定义类Boy的成员函数disp,同时也为类Gril的友元函数,
{ //形参为Gril类对象
cout<<"男孩的姓名:"<<name<<endl; //函数disp作为Boy类的成员函数 ,可以访问Boy类对象的私有成员
cout<<"男孩的年龄:"<<age<<endl; //注释同上
cout<<"女孩的姓名:"<<x.name<<endl;//函数disp作为Gril类的友元函数,可以访问Gril类对象的私有成员
cout<<"女孩的年龄:"<<x.age<<endl; //注释同上
}
int main()
{
Boy b("陈大林",);
Gril g("张晓好",);
b.disp(g);//调用Boy类的对象b的成员函数和Gril类的友元函数disp,实参是Gril类的对象g
//因为函数disp是Boy类的成员函数,所以无需通过传递对象,可以直接访问自己的私有数据成员
return ;
}

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 友元类的应用

#include<iostream>
#include<string>
using namespace std;
class Gril; //对友元类的提前引用声明
class Boy{
public:
Boy(char* n,int a)
{
name=new char[strlen(n)+];
strcpy(name,n);
age = a;
}
~Boy()
{
delete []name;
}
void display1(Gril &); //声明函数display1为类Boy的成员函数
void display2(Gril &); //声明函数display2为类Boy的成员函数
private:
char* name;
int age;
};
class Gril{
public:
Gril(char* n,int a)
{
name=new char[strlen(n)+];
strcpy(name,n);
age = a;
}
~Gril()
{
delete []name;
} friend class Boy; //声明Boy为类Gril的友元类,则类Boy中的所有成员函数为Gril类的友元成员函数 private:
char* name;
int age;
};
void Boy::display1(Gril &x)
{
cout<<"男孩的姓名是:"<<name<<endl;
cout<<"女孩的姓名是:"<<x.name<<endl;
} void Boy::display2(Gril &x)
{
cout<<"男孩的年龄是:"<<age<<endl;
cout<<"女孩的年龄是:"<<x.age<<endl;
}
int main()
{
Boy b("陈大林",);
Gril g("张晓好",); b.display1(g);
b.display2(g); return ;
}

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

#include<iostream>
#include<string>
using namespace std;
class Gril; //对友元类的提前引用声明
class Boy{
public:
Boy(char* n,int a)
{
name=new char[strlen(n)+];
strcpy(name,n);
age = a;
}
~Boy()
{
delete []name;
}
void display(Gril &); //声明函数display为类Boy的成员函数
private:
char* name;
int age;
};
class Gril{
public:
Gril(char* n,int a)
{
name=new char[strlen(n)+];
strcpy(name,n);
age = a;
}
~Gril()
{
delete []name;
} friend class Boy; //声明Boy为类Gril的友元类,则类Boy中的所有成员函数为Gril类的友元成员函数 private:
char* name;
int age;
};
void Boy::display(Gril &x)
{
cout<<"男孩的姓名是:"<<name<<endl;
cout<<"男孩的年龄是:"<<age<<endl;
cout<<"女孩的姓名是:"<<x.name<<endl;
cout<<"女孩的年龄是:"<<x.age<<endl;
}
int main()
{
Boy b("陈大林",);
Gril g("张晓好",); b.display(g); return ;
} /*
说明:友元关系是单向的,不具有交换性。若声明了类X是类Y的友元类(即在类Y定义中声明X为friend类),
不等于类Y一定是X的友元,这就要看在类X中是否有相应的声明。 友元关系也不具有传递性,若类X是类Y的友元,类Y是类Z的友元,不一定类X是类Z的友元。如果想让类X
是类Z的友元类,应在类Z中作出声明。
*/

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. Java从入门到精通——基础篇之JSTL标签

    一.语言基础 EL(Expression Language)表达式,目的:为了使JSP写起来更加简单.提供了在 JSP 中简化表达式的方法. 二.分类 核心标签库:提供条件判断.属性访问.URL处理及 ...

  2. Java从入门到精通——数据库篇之OJDBC版本区别

    classes12.jar,ojdbc14.jar,ojdbc5.jar和ojdbc6.jar的区别,之间的差异 在使用Oracle JDBC驱动时,有些问题你是不是通过替换不同版本的Oracle  ...

  3. 【jquery】javaScript中prototype的妙用 巧妙运用prototype属性原型链创建对象

    prototype  可以有好多有优化实现方法 http://blog.csdn.net/liuqiwen0512/article/details/8089690 在 JavaScript 中,每个函 ...

  4. MVC学习系列——ActionResult扩展

    首先,MVC扩展性非常强. 我从ActionResult扩展入手,因为我们知道微软ActionResult和其子类,有时候并不能满足所有返回值. 比如:我需要返回XML. 因此,现在我扩展XMLRes ...

  5. Ubuntu实用快捷键

    ALT + TAB: 切换程序窗口Win+w: 显示所有的工作空间,可轻松进行切换 ===== Terminal终端 ===== CTRL + ALT + T: 打开终端 TAB: 自动补全命令或文件 ...

  6. Eclipse启动的时候窗口一闪就关的解决办法(转)

    有时候会碰到如题这种问题,从网上查知解决办法,非常管用 为eclipse.exe创建一个快捷方式,然后快捷方式上右键-属性,在目标栏填入 E:\eclipse\eclipse.exe -vm &quo ...

  7. bnuoj 4207 台风(模拟题)

    http://www.bnuoj.com/bnuoj/problem_show.php?pid=4207 [题意]:中文题,略 [题解]:模拟 [code]: #include <iostrea ...

  8. android开发 java与c# 兼容AES加密

    由于android客户端采用的是AES加密,服务器用的是asp.net(c#),所以就造成了不一致的加密与解密问题,下面就贴出代码,已经试验过. using System; using System. ...

  9. java笔记之类和对象

    现在编程的思想分成了两大阵营,面向过程和面向对象.现在谈谈啥是面向对象. 作为一只单身狗,谈“对象”还是很伤心很伤心的(:′⌒`)...... 先看看百度怎么说? 好吧,百度说的太抽象,我换个简单的说 ...

  10. 2109&2535: [Noi2010]Plane 航空管制 - BZOJ

    Description世博期间,上海的航空客运量大大超过了平时,随之而来的航空管制也频频发生.最近,小X就因为航空管制,连续两次在机场被延误超过了两小时.对此,小X表示很不满意. 在这次来烟台的路上, ...