正在加载……
专注、离线、切勿分心
      一般来说,类的私有成员只能在类的内部访问,类外的函数是不能访问它们的。但是,可以将一个函数定义为类的友元函数,这时该函数就可以访问该类的私有成员了。同时,也可以把另一个类B定义为本类A的友元类,这样B类就可以访问A类的任何成员

***************函数传参存在隐士转换,形参必须声明const才能真正绑定
1)当函数的形参类型是const类型时,可以通过const类型的实参调用函数,也可以用非const类型的实参调用函数。

2)当函数的形参类型是非const类型时,只能用非const类型的实参调用函数。

综上两点可以看出,在不需要修改实参的情况下,函数的形参类型声明为const类型时,初始化的限制条件会相对放松些。所以有一种情况应当引起重视,就是应该吧不需要修改的引用形参定义为const引用。因为普通的非const引用会限制函数的应用,比如,这样的形参不能用const对象初始化,不能用字面值或产生右值的表达式初始化。

友元之非成员函数:


       在类的定义中用friend声明了一个外部函数或其他类的成员函数(public和private均可)后,这个外部函数称为类的友元函数。

友元函数声明的基本格式为:
friend 函数原型;
友元函数中可访问类的private成员
     将类比作一个家庭,类的private成员相当于家庭的秘密,一般的外人是不允许探听这些秘密的,只有friend(朋友)才有能力探听这些秘密。
#include <iostream>
#include<math.h>
using namespace std;
class point
{
        private:
                int _x;
                int _y;
        public:
                point(int x=0,int y=0):_x(x),_y(y)
                {
                        cout<<"point(int,int)"<<endl;
                }
                void print()
                {
                        cout<<"("<<_x<<","<<_y<<")"<<endl;               
                }
                friend float dis(const point & a1,const point & a2);
};
float dis(const point& a1,const point& a2)
{
        return sqrt((a1._x-a2._x)*(a1._x-a2._x)+
                        (a1._y-a2._y)*(a1._y-a2._y));
}
int main()
{
        point a1(1,2);
        point a2(4,6);
        cout<<"dis="<<dis(a1,a2)<<endl;
        return 0;
}



友元之成员函数:
A类的成员函数(公有)作为B类的友元函数时,必须先定义A类,而不仅仅是声明它。
注意将其他类的成员函数申明为本类的友元函数后,该友元函数并不能变成本类的成员函数。也就是说,朋友并不能变成家人。
#include<iostream>
#include<math.h>
using namespace std;
class point; //前向声明
class line
{
        public:
                float distance(const point &,const point &); //类line的成员作为point类的友元函数
                //必须先定义line类,而不仅仅是声明;
#if 0
                {
                        return sqrt(
                                        (p2._x-p1._x)*(p2._x-p1._x) +
                                        (p2._y-p1._y)*(p2._y-p1._y)
                                        );   //_x和_y在这里未知
                }
#endif
};
class point
{
        public:
                point(int x=0,int y=0):_x(x),_y(y)
                {
                        cout<<"point(int,int)"<<endl;
                }
                ~point()
                {
                        cout<<"~point()"<<endl;
                }
                void print()
                {
                        cout<<"("<<_x<<","<<_y<<")"<<endl;
                }
                friend float line::distance(const point &,const point &);  //实现必须要放到point类外面,因为里面用到了point
#if 0
                {//类point还没有定义完,这里point未知
                        return sqrt(
                                        (p2._x-p1._x)*(p2._x-p1._x) +
                                        (p2._y-p1._y)*(p2._y-p1._y)
                                        );   //_x和_y在这里未知
                }
#endif
        private:
                int _x;
                int _y;
};
#if 1
float line::distance(const point &p1,const point &p2)
{
        return sqrt(
                        (p2._x-p1._x)*(p2._x-p1._x) +
                        (p2._y-p1._y)*(p2._y-p1._y)
                        );
}
#endif
int main()
{
        point p1(1,2);
        point p2(3,4);
        line tmp;
        cout<<tmp.distance(p2,p1)<<endl;
        return 0;
}
//point(int,int)
//point(int,int)
//2.82843
//~point()
//~point()

Line类的成员函数dis(…)的实现必须在类外进行,且必须在Point类的定义之后。因为其参数中包含了Point这种类型。
Line类的dis()函数本来是不能访问Point.x和Pint.y这种Point类的private成员的,但在Point类中将dis()申明为友元函数后就能访问了。但dis()函数依然不是Point类的成员函数。也就是说,dis() 是Point类的朋友了,可以访问Point类的私有成员变量x和y了。


友元函数的重载:
要想使得一组重载函数全部成为类的友元,必须一一声明,否则只有匹配的那个函数会成为类的友元,编译器仍将其他的当成普通函数来处理。
class Exp
{
     public:
     firend void test(int);        
};
void test();
void test(int);
void test(double);


友元类:
类A作为类B的友元时,类A称为友元类。A中的所有成员函数都是B的友元函数,都可以访问B中的所有成员。
A可以在B的public部分或private部分进行声明,方法如下:

friend [class]<类名>; //友元类类名

#include <iostream>
#include<math.h>
using namespace std;
class point;   //前向声明
class line
{
        public:
        //让类的每一个成员函数都成为point类的友元函数
        float dis(const point &a1,const point &a2);
        void set(point &p1,int x,int y);
};
class point
{
        private:
                int _x;
                int _y;
        public:
                point(int x=0,int y=0):_x(x),_y(y)
                {
                        cout<<"point(int,int)"<<endl;
                }
                void print()
                {
                        cout<<"("<<_x<<","<<_y<<")"<<endl;
                }
                //友元类
                friend class line;           //friend line; 也可以
};
float line::dis(const point &a1,const point &a2)
{
        return sqrt((a1._x-a2._x)*(a1._x-a2._x)+
                                (a1._y-a2._y)*(a1._y-a2._y));
}
void line::set(point &p1,int x,int y)
{
        p1._x=x;
        p1._y=y;
}

int main()
{
        point a1(1,2);
        point a2(4,6);
        cout<<"a1 ";
        a1.print();
        line aa;
        cout<<"dis="<<aa.dis(a1,a2)<<endl;
        cout<<"重新设置a1的值:"<<endl;
        aa.set(a1,3,4);
        cout<<"a1 ";
        a1.print();
        return 0;
}




友元是否破坏了封装性:

     不可否认,友元在一定程度上将类的私有成员暴露出来,破坏了信息隐藏机制,似乎是种“副作用很大的药”,但俗话说“良药苦口”,好工具总是要付出点代价的,拿把锋利的刀砍瓜切菜,总是要注意不要割到手指的。
     但是友元的存在,使得类的接口扩展更为灵活,使用友元进行运算符重载从概念上也更容易理解一些,而且,C++规则已经极力地将友元的使用限制在了一定范围内,它是单向的、不具备传递性、不能被继承,所以,应尽力合理使用友元



posted on 2018-04-21 09:14  正在加载……  阅读(205)  评论(0编辑  收藏  举报