一般来说,类的私有成员只能在类的内部访问,类外的函数是不能访问它们的。但是,可以将一个函数定义为类的友元函数,这时该函数就可以访问该类的私有成员了。同时,也可以把另一个类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++规则已经极力地将友元的使用限制在了一定范围内,它是单向的、不具备传递性、不能被继承,所以,应尽力合理使用友元。