本文主要介绍面向对象编程的友元的使用,以及友元的特性和分类,提供C++代码。
1 为什么引进友元
面向对象编程(OOP)的三大特性中的封装,是通过类实现对数据的隐藏和封装。一般定义类的成员变量为私有成员,成员函数为公有函数,通过公有函数作为类的接口实现与外部交互。一些情况下,类外的某些函数需要频繁访问类的成员变量,因此引入了友元的概念,将类外的函数定义为该类的友元函数,从而实现对该类私有成员的访问。由此,还引入友元类,就是一个类是另一个类的友元类,友元类可以访问另一个类的所有成员。友元函数和友元类称为友元。
友元的作用是提高了程序的运行效率(减少了类型检查和安全检查的耗时),但是友元破坏了类的封装和隐藏性,让非类成员函数可以类的私有成员。
友元是C++语言中的一种关系,友元关系发生在函数与类之间或者类与类之间。友元关系是单向的,不能传递。
与类有友元关系的函数称为友元函数,与类有友元关系的类称为友元类。
2 友元的性质
(1)在被访问类中以friend关键字修饰友元类或者友元函数
(2)友元不属于该类,且不受该类的访问限制,可以直接访问具体类的所有成员
(3)友元关系不能被继承
(4)友元关系是单向的,不具有交换性,只能是一个函数访问一个类的所有成员,或者一个类允许访问另一个类的所有成员,反之不行
(5)友元关系不具有传递性
3 友元的本质
友元的本质是提供不属于该类成员,包括全局函数、其他类的成员函数、其他类,访问本类所有成员和成员属性的属性。
4 友元分类
4.1 全局函数为友元函数
全局函数拥有访问一个类所有成员的能力,需要在被访问类中用关键字friend声明f被访问类的友元函数。一个类可以拥有多个友元函数。一个函数也可以是多个类的友元函数。
代码如下:
#include <iostream>
#include <string>
using namespace std;class A
{private:string name_;int age_;public:A(const string name,const int age) : name_(name), age_(age) {cout << "A构造函数 初始化参数" << endl;};virtual ~A(){cout << "A析构函数 " << endl;}void func(){std::cout << "A's func()" << std::endl;}friend void get_members_global(A & a); // 友元全局函数,可以访问A的私有成员变量和所有的公有成员};// 全局函数作为友元函数
void get_members_global(A & a)
{cout << a.name_ << " is " << a.age_ << " years old " << endl;a.func();
}int main()
{A a("Hubery",45);cout << "***************************全局函数作为友元函数***************************" << endl;get_members_global(a);return 0;
}
运行结果:
4.2 类的成员函数为友元函数
类成员函数作为类的友元声明时只需在友元的名称前加上关键字friend,其格式如下:
friend 类型 类名::函数名(形式参数);
一个函数可以是多个类的友元函数,只需要在各个类中分别声明。
代码如下:
#include <iostream>
#include <string>
using namespace std;class A;class C
{public://类的成员函数作为友元函数void get_members_member(A &a);C(){cout << "C构造函数" << endl;}~C(){cout << "C析构函数" << endl;}
};class A
{private:string name_;int age_;public:A(const string name,const int age) : name_(name), age_(age) {cout << "A构造函数 初始化参数" << endl;};virtual ~A(){cout << "A析构函数 " << endl;}void func(){std::cout << "A's func()" << std::endl;}friend void C::get_members_member(A &a); // C的成员函数做友元函数,可以访问所有成员};// 类的成员函数作为友元函数
void C::get_members_member(A &a)
{cout << a.name_ << " is " << a.age_ << " years old " << endl;a.func();
}int main()
{A a("Hubery",45);C c;cout << "***************************类的成员函数作为友元函数***************************" << endl;c.get_members_member(a);return 0;
}
运行结果:
这里用到类的前向声明。前向声明,是一种不完全型(forward declaration)声明,即只需提供类名(无需提供类实现)即可。前向声明功能有限:
(1)不能定义类的对象。
(2)可以用于定义指向这个类型的指针或引用。
(3)用于声明(不是定义)使用该类型作为形参或者返回类型的函数。
4.3 类作为友元
友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的隐藏信息(包括私有成员和保护成员)。当希望一个类可以访问另一个类的私有成员、保护成员时,可以将该类声明为另一类的友元类。
定义友元类的语句格式如下:
friend class 类名;
friend和class是关键字,类名必须是程序中的一个已定义的类。
代码如下:
#include <iostream>
#include <string>
using namespace std;class B; // 前向声明class A
{private:string name_;int age_;public:A(const string name,const int age) : name_(name), age_(age) {cout << "A构造函数 初始化参数" << endl;};virtual ~A(){cout << "A析构函数 " << endl;}void func(){std::cout << "A's func()" << std::endl;}friend class B; // 声明B为A的友元类,B中成员函数可以访问A中所有的成员};class B
{private:string name_;int age_;public:B(const string name,const int age) : name_(name), age_(age) {cout << "B构造函数 初始化参数" << endl;};virtual ~B(){cout << "B析构函数 " << endl;}void func(){std::cout << "B's func()" << std::endl;};// 友元类void get_members_class(A & a){cout << a.name_ << " is " << a.age_ << " years old " << endl;a.func();};};int main()
{A a("Hubery",45);B b("Tom",24);cout << "***************************友元类***************************" << endl;b.get_members_class(a);return 0;
}
运行结果: