C++学习笔记33——友元

来源:互联网 发布:苹果mac笔记本切换系统 编辑:程序博客网 时间:2024/06/01 13:44

1,友元的基本概念

友元:类授权访问其非public成员的机制。类和函数都可以被指定为友元。友元拥有与成员一样的访问权相。

友元可以分成3类:
(1)普通函数(非类的成员函数)成为一个类类型B的友元;
(2)一个类A 的成员函数,成为另一个类类型B的友元;
(3)一个类A,成为一个类类型B的友元,如此,则该类A 的所有成员函数都成为类B的友元;

友元函数或友元类在类的定义体中,用friend关键字声明。

注意:
(1)友元不是类B的成员,所以其没有类B的对象的this指针;
(2)所以,虽然友元可以访问类B的私有成员,但需要程序将类B的对象作为参数传递给友元函数,当然,全局变量除外;
(3)同样由于没有this指针,在友元中使用私有成员时,必须指明对象,而不能像在类B的成员函数里那样隐式地使用this指针;
(4)由于友元不是类的成员,所以friend声明放在public段还是private段都行,且没有区别;
(5)friend关键字不能出现的类定义之外,即,只在声明友元的时候使用friend,定义时不能写。

2,普通函数用作友元

/*******************************************************************///      友元/*******************************************************************/class Point{public:Point(double xx, double yy) :x(xx), y(yy) {}//定义构造函数void show_pos() { cout << "x: " << x << " " << "y:" << y << endl; }//定义普通成员函数friend double Distance(Point &A, Point &B);//声明友元函数friend double Distance_2(Point &A, Point &B)//定义友元函数{double x = A.x - B.x;double y = A.y - B.y;return sqrt(x*x + y*y);}private:double x, y;};// 在main函数中调用友元函数int main(){Point A(0, 0);Point B(3, 4);cout << Distance(A, B) << endl;cout << Distance_2(A, B) << endl;system("pause");return 0;}//定义友元函数double Distance(Point &A, Point &B){double x = A.x - B.x;double y = A.y - B.y;return sqrt(x*x + y*y);}


最终输出:5
                    5
从例子中可以看出:
(1)Distance()虽然是类Point的友元,但是,还是必须将Point类的对象A和B传递给Distance(),其才能使用Point类的私有成员。
(2)在Point类中声明Distance()为友元的时候,Distance()可以是既没有声明、也没有被定义的状态,即友元不必预先声明,友元声明中声明的函数被视为已使用 extern 关键字声明,此时该友元函数具有文件作用域,或者说命名空间作用域。
(3)但是,在定义友元函数时,类必须是已经声明好的。因为友元要用到类中的成员。总之:类的定义要在友元的定义之前。
(4)友元函数也可以直接在类中定义,如Distance_2()。类声明中定义的友元函数不被认为在封闭类的范围内;它们在文件范围内。

3,类的成员函数用作友元

/*******************************************************************///      友元/*******************************************************************/class Point;//提前声明point类,因为Line的成员函数要用到//定义友元函数所在的类class Line{public:Line(double kk, double bb) :k(kk), b(bb) {}//构造函数bool judege_point_in_line(Point &A);//判断点是否在线上,要用到point的私有成员private:double k, b;};//定义友元函数要获取权限的类class Point{public:Point(double xx, double yy) :x(xx), y(yy) {}//定义构造函数void show_pos() { cout << "x: " << x << " " << "y:" << y << endl; }//定义普通成员函数friend double Distance(Point &A, Point &B);//声明友元函数friend double Distance_2(Point &A, Point &B)//定义友元函数{double x = A.x - B.x;double y = A.y - B.y;return sqrt(x*x + y*y);}friend bool Line::judege_point_in_line(Point &A);//声明另一个类的成员函数为友元private:double x, y;};// 定义友元函数bool Line::judege_point_in_line(Point &p){return (p.y == k*p.x + b);}// 在main()函数中调用友元函数int main(){Point A(0, 0);Point B(3, 4);Line l1(1, 0);// 调用一般友元函数cout << Distance(A, B) << endl;cout << Distance_2(A, B) << endl;// 调用类的成员函数友元cout << l1.judege_point_in_line(A) << endl;cout << l1.judege_point_in_line(B) << endl;system("pause");return 0;}
最终输出:5
                    5
                    1
                    0

编码的顺序逻辑如下:
要定义Line::judege_point_in_line(Point &p)友元函数,必须要先定义Point类,因为函数的输入参数为Point型,且函数体中用到了Point的成员;
在定义Point类时声明judege_point_in_line为友元函数;
要定义Point类必须先定义Line类,因为Point类中出现了Line类的成员函数judege_point_in_line;
要定义Line类必须先提前声明Point类,因为Line中的成员函数judege_point_in_line(Point &p)使用了Point类型为输入参数;

总结下来,如果要声明类A中的成员函数mem_fun()为类B的友元,则正确的编码顺序是:
(1)提前声明类B(仅仅是声明不是定义);
(2)定义类A,在类A中声明(无法定义,因为B还没定义)注定会成为B的友元的那个成员函数mem_fun();
(3)定义类B,在类B中声明A中的成员函数mem_fun()为友元;
(4)定义B的成员函数mem_fun()。

4,整个类用作友元

如果类A成为类B的友元,则类A中的每个成员函数都能访问类B的私有和保护成员。
编码的顺序与什么类的成员函数为友元相同。
将Point类的定义部分修改为如下形式,执行结果不变。
class Point{public:Point(double xx, double yy) :x(xx), y(yy) {}//定义构造函数void show_pos() { cout << "x: " << x << " " << "y:" << y << endl; }//定义普通成员函数friend double Distance(Point &A, Point &B);//声明友元函数friend double Distance_2(Point &A, Point &B)//定义友元函数{double x = A.x - B.x;double y = A.y - B.y;return sqrt(x*x + y*y);}//friend bool Line::judege_point_in_line(Point &A);//声明另一个类的成员函数为友元friend class Line;//将整个类声明为友元,class好像写不写都行,存疑private:double x, y;};

5,重载函数与友元

重载函数本质上是不同的函数,只是重名而已。所以必须将重载函数集中每一个想要设为友元的函数都声明为友元。只什么一个却期待重载函数集中的每个函数都成为友元是不对的。——这就好比你和一个叫王海燕的人是朋友,但不代表你和每一个与王海燕重名的人都是朋友。

0 0
原创粉丝点击