1. 引入友元函数原因
在实现类之间数据共享时,减少系统开销,提高效率。
具体来说:为了使其他类的成员函数直接访问该类的私有变量。
即:允许外面的类或函数去访问类的私有变量和保护变量,从而使两个类共享同一函数。
优点:能够提高效率,表达简单、清晰。
缺点:友元函数破环了封装机制,尽量不使用友元函数,除非不得已的情况下才使用友元函数。
2. 使用友元函数时机
1)运算符重载的某些场合需要使用友元。
2)两个类要共享数据的时候
3. 如何使用友元函数
3.1 友元函数的参数
因为友元函数没有this指针,则参数要有三种情况:
1)要访问非static成员时,需要对象做参数;--常用(友元函数常含有参数)
2)要访问static成员或全局变量时,则不需要对象做参数
3)如果做参数的对象是全局对象,则不需要对象做参数
3.2 友元函数的位置
因为友元函数是类外的函数,所以它的声明可以放在类的私有段或公有段且两者没有区别。
3.3 友元函数的调用
可以直接调用友元函数,不需要通过对象或指针。
3.4 友元函数的分类
根据这个函数的来源不同,可以分为三种方法:
3.4.1 普通函数友元函数
1)目的:使普通函数能够访问类的友元
2)语法:
声明位置:公有私有均可,常写为公有
声明: friend + 普通函数声明
实现位置:可以在类外或类中
实现代码:与普通函数相同(不加不用friend和类::)
调用:类似普通函数,直接调用
3)示例代码:
1 #include <iostream> 2 3 using namespace std; 4 5 class Example 6 { 7 public: 8 Example(const int val = 0) : num(val){}; 9 virtual ~Example() {}; 10 friend void display(const Example& exam); 11 12 private: 13 int num; 14 15 }; 16 17 void display(const Example& exam) 18 { 19 cout << exam.num << endl; 20 } 21 22 void main() 23 { 24 Example example(10); 25 display(example); 26 }
3.4.2 类Y的所有成员函数都为类X友元函数—友元类
1)目的:使用单个声明使Y类的所有函数成为类X的友元
它提供一种类之间合作的一种方式,使类Y的对象可以具有类X和类Y的功能
具体来说:
前提:A是B的友元(=》A中成员函数可以访问B中有所有成员,包括私有成员和公有成员)
则:在A中,借助类B,可以直接使用“B . 私有变量”的形式访问私有变量
2)语法:
声明位置:公有私有均可,常写为私有(把类看成一个变量)
声明: friend + 类名---不是对象啊
调用:
3)示例代码:
1 #include <iostream> 2 3 using namespace std; 4 5 class Boy; 6 class Girl 7 { 8 public: 9 Girl(){ name = "Lucy"; age = 18; }; 10 virtual ~Girl(){}; 11 12 private: 13 char * name; 14 int age; 15 friend Boy; //声明类Boy是类Girl的友元 16 17 }; 18 19 class Boy 20 { 21 public: 22 Boy(){ name = "Jimmy"; age = 20; }; 23 virtual ~Boy(){}; 24 void display(Girl& girl); 25 26 private: 27 char * name; 28 int age; 29 30 }; 31 32 void Boy::display(Girl& girl) //函数disp()为类Boy的成员函数,也是类Girl的友元函数 33 { 34 //正常情况,Boy的成员函数disp中直接访问Boy的私有变量 35 cout << "Boy's name is: " << name << ", age: " << age << "." << endl; 36 37 //借助友元,在Boy的成员函数disp中,借助Girl的对象,直接访问Girl的私有变量 38 //而正常情况下,只允许在Girl的成员函数中访问Girl的私有变量 39 cout << "Girl's name is: " << girl.name << ", age: " << girl.age << "." << endl; 40 41 } 42 43 44 int main() 45 { 46 Boy boy; 47 Girl girl; 48 boy.display(girl); //b调用自己的成员函数,但是以girl为参数,友元机制体现在函数display中 49 50 return 0; 51 }
3.4.3 类Y的一个成员函数为类X的友元函数
1)目的:使类Y的一个成员函数成为类X的友元
具体而言:而在类Y的这个成员函数中,借助参数X,可以直接以X。私有变量的形式访问私有变量
2)语法:
声明位置:声明在公有中 (本身为函数)
声明:friend + 成员函数的声明
调用:先定义Y的对象y---使用y调用自己的成员函数---自己的成员函数中使用了友元机制
3)示例代码:
1 #include <iostream> 2 3 using namespace std; 4 5 class Girl; 6 class Boy 7 { 8 public: 9 Boy(){ name = "Jimmy"; age = 20; }; 10 virtual ~Boy(){}; 11 void display(Girl& girl); 12 13 private: 14 char * name; 15 int age; 16 17 }; 18 19 class Girl 20 { 21 public: 22 Girl(){ name = "Lucy"; age = 18; }; 23 virtual ~Girl(){}; 24 25 private: 26 char * name; 27 int age; 28 friend void Boy::display(Girl& girl); //声明类Boy的成员函数是类Girl的友元函数 29 30 }; 31 32 //函数display()为类Boy的成员函数,也是类Girl的友元函数 33 void Boy::display(Girl& girl) 34 { 35 //正常情况,Boy的成员函数disp中直接访问Boy的私有变量 36 cout << "Boy's name is: " << name << ", age: " << age << "." << endl; 37 38 //借助友元,在Boy的成员函数disp中,借助Girl的对象,直接访问Girl的私有变量 39 //而正常情况下,只允许在Girl的成员函数中访问Girl的私有变量 40 cout << "Girl's name is: " << girl.name << ", age: " << girl.age << "." << endl; 41 42 } 43 44 int main() 45 { 46 Boy boy; 47 Girl girl; 48 boy.display(girl); //b调用自己的成员函数,但是以girl为参数,友元机制体现在函数display中 49 50 return 0; 51 }
4. 模板友元函数
在微软官方网站,有一个例子是用来说明如何建立模板友元的。下边是
4.1 模板友元函数
例子:
1 #include <iostream> 2 using namespace std; 3 4 template <class T> class Array { 5 T* array; 6 int size; 7 8 public: 9 Array(int sz) : size(sz) { 10 array = new T[size]; 11 memset(array, 0, size * sizeof(T)); 12 } 13 14 Array(const Array& a) { 15 size = a.size; 16 array = new T[size]; 17 memcpy_s(array, a.array, sizeof(T)); 18 } 19 20 T& operator[](int i) { 21 return *(array + i); 22 } 23 24 int Length() { return size; } 25 26 void print() { 27 for (int i = 0; i < size; i++) 28 cout << *(array + i) << " "; 29 30 cout << endl; 31 } 32 33 template<class T> 34 friend Array<T>* combine(Array<T>& a1, Array<T>& a2); 35 }; 36 37 template<class T> 38 Array<T>* combine(Array<T>& a1, Array<T>& a2) { 39 Array<T>* a = new Array<T>(a1.size + a2.size); 40 for (int i = 0; i < a1.size; i++) 41 (*a)[i] = *(a1.array + i); 42 43 for (int i = 0; i < a2.size; i++) 44 (*a)[i + a1.size] = *(a2.array + i); 45 46 return a; 47 } 48 49 int main() { 50 Array<char> alpha1(26); 51 for (int i = 0; i < alpha1.Length(); i++) 52 alpha1[i] = 'A' + i; 53 54 alpha1.print(); 55 56 Array<char> alpha2(26); 57 for (int i = 0; i < alpha2.Length(); i++) 58 alpha2[i] = 'a' + i; 59 60 alpha2.print(); 61 Array<char>*alpha3 = combine(alpha1, alpha2); 62 alpha3->print(); 63 delete alpha3; 64 }
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m n o p q r s t u v w x y z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z
这个例子中,比较值得注意的是在类中声明模板友元函数部分:
1 template<class T> 2 friend Array<T>* combine(Array<T>& a1, Array<T>& a2);
template<class T>这一句不可丢。友元函数的定义为:
template<class T> Array<T>* combine(Array<T>& a1, Array<T>& a2) { Array<T>* a = new Array<T>(a1.size + a2.size); for (int i = 0; i < a1.size; i++) (*a)[i] = *(a1.array + i); for (int i = 0; i < a2.size; i++) (*a)[i + a1.size] = *(a2.array + i); return a; }
4.2 模板友元类
例子:
1 // template_friend3.cpp 2 // compile with: /EHsc 3 #include <iostream> 4 using namespace std; 5 6 template <class T> 7 class X 8 { 9 private: 10 T* data; 11 void InitData(int seed) { data = new T(seed); } 12 public: 13 void print() { cout << *data << endl; } 14 template <class U> friend class Factory; 15 }; 16 17 template <class U> 18 class Factory 19 { 20 public: 21 U* GetNewObject(int seed) 22 { 23 U* pu = new U; 24 pu->InitData(seed); 25 return pu; 26 } 27 }; 28 29 int main() 30 { 31 Factory< X<int> > XintFactory; 32 X<int>* x1 = XintFactory.GetNewObject(65); 33 X<int>* x2 = XintFactory.GetNewObject(97); 34 35 Factory< X<char> > XcharFactory; 36 X<char>* x3 = XcharFactory.GetNewObject(65); 37 X<char>* x4 = XcharFactory.GetNewObject(97); 38 x1->print(); 39 x2->print(); 40 x3->print(); 41 x4->print(); 42 }
65
97
A
a
5. 其他知识点
1)友元函数和类的成员函数的区别:成员函数有this指针,而友元函数没有this指针。
2)A是B的友元《=》A是B的朋友《=》借助B的对象,在A中可以直接通过“B.成员变量”(可以是公有,也可以为私有变量) 的方式访问B。