喜欢美食, 旅行..
分类: C/C++
2009-10-19 09:44:21
1.需要友元的原因
有时候,普通函数需要直接访问一个类的保护或私有数据成员。如果没有友元机制,则只能将类的数据成员声明为公共的,从而,任何函数都可以无约束地访问它。
普通函数需要直接访问类的保护或私有数据成员的原因主要是为提高效率。
例如,下面的代码是做矩阵和向量的乘法。矩阵类和向量类分别为Matrix和Vector,乘法操作的函数只能是普通函数,因为一个函数不可能既是这个类的成员又是那个类的成员:
//**********************
//** ch15_7.cpp **
//**********************
#include
#include
class Vector{
public:
Vector(int);
~Vector(){ delete[]v; } //将堆中数组空间返还
Vector(Vector & );
int Size(){ return sz; }
void Display();
int& Elem(int); //返回向量元素
protected:
int* v; //指向一个数组,表示向量
int sz; //元素个数
};
Vector::Vector(int s)
{
if(s<=0){
cerr <<"bad Vector size.\n";
exit(1);
}
sz=s;
v =new int[s]; //从堆中分配一个数组存放向量元素
}
int& Vector::Elem(int i) //引用返回的目的是返回值可以作左值
{
if(i<0||sz<=i){
cerr <<"Vector index out of range.\n";
exit(1);
}
return v[i];
}
Vector::Vector(Vector& vec)
{
v = new int[sz=vec.sz];
memcpy((void*)v,(void*)vec.v,sz*sizeof(int));
}
void Vector::Display()
{
for(int i=0; i
class Matrix{
public:
Matrix(int,int);
Matrix(Matrix & );
~Matrix(){ delete[]m; }
int SizeL(){ return szl; }
int SizeR(){ return szr; }
int& Elem(int,int);
protected:
int* m;
int szl;
int szr;
};
Matrix::Matrix(int i,int j)
{
if(i<=0||j<=0){
cerr <<"bad Matrix size.\n";
exit(1);
}
szl=i;
szr=j;
m =new int[i*j];
}
Matrix::Matrix(Matrix& mat)
{
szl=mat.szl;
szr=mat.szr;
m=new int[szl*szr];
memcpy((void*)m,(void*)mat.m, szl*szr*sizeof(int));
}
int& Matrix::Elem(int i,int j)
//引用返回的目的是返回值可以作左值
{
if(i<0||szl<=i||j<0||szr<=j){
cerr <<"Matrix index out of range.\n";
exit(1);
}
return m[i*szr+j];
}
Vector Multiply(Matrix& m, Vector& v)
//矩阵乘向量的普通函数
{
if(m.SizeR()!=v.Size()){
cerr <<"bad multiply Matrix with Vector.\n";
exit(1);
}
Vector r(m.SizeL()); //创建一个存放结果的空向量
for(int i=0; i
for(int j=0; j
}
return r;
}
void main()
{
Matrix ma(4,3);
ma.Elem(0,0)=1; ma.Elem(0,1)=2; ma.Elem(0,2)=3;
ma.Elem(1,0)=0; ma.Elem(1,1)=1; ma.Elem(1,2)=2;
ma.Elem(2,0)=1; ma.Elem(2,1)=1; ma.Elem(2,2)=3;
ma.Elem(3,0)=1; ma.Elem(3,1)=2; ma.Elem(3,2)=1;
Vector ve(3);
ve.Elem(0)=2; ve.Elem(1)=1; ve.Elem(2)=0;
Vector va =Multiply(ma,ve);
va.Display();
}
运行结果为:
4 1 3 4
Mamx中的m和Vector中的v是保护数据。由于Multiply()不是Matrix和Vector类的成员,不能直接操纵m[i][j]和v[j],只能通过m.Elem(i,j)和v.Elem(j)来访问矩阵和向量的元素。 Elem()函数定义中要对下标进行合法性检查,所以,Multiply()函数要频繁地调用函数和进行下标检查。做一次上例中的小乘法,矩陈(4,3)乘以向量(3)得到向量(4),其Elem()和Size()成员函数要调用3+1+4*(1+(1+2)x*3)=44次,显然效率不高。于是希望乘
法不要调用Elem()函数,能直接访问两个类的保护数据成员。
2. 友元的使用
在类里声明一个普通函数,标上关键字friend,就成了该类的友元,可以访问该类的一切成员。在上节中,将Multiply()函数声明为Matrix和Vector两个类的友元,就能使Multiply()函数既可访问Matrix的保护数据成员,又可访问Vector类的保护数据成员了。
例如,下面的代码将程序chl5-7.cpp中的Multiply()改为友元:
#include
class Vector{
public:
Vector(int);
~Vector(){ delete[]v; } //将堆中数组空间返还
Vector(Vector & );
int Size(){ return sz; }
void Display();
int& Elem(int); //返回向量元素
friend Vector Multiply(Matrix& m,Vector& v);
protected:
int* v; //指向一个数组,表示向量
int sz; //元素个数
};
class Matrix
{
public:
Matrix(int,int);
~Matrix(){delete[]m;}
//int SizeL(){returnszl;}不需要
//int sizeR(){returnszr;}不需要
int&Elem(int,int);
friend Vector Multiply(Matrix& m,Vector& v);
protected:
int x m;
int szl;
int szr;
};
//省略成员函数定义
VectorMultiply(Matrix&m,Vector&v)//友元定义
{
if(m.szr!;v.sz) //直接访问保护数据
{
cerr<<”badmultiplying MatrixwithVector./n";
exit(1);
}
Vector r(m.sz1); //直接访问保护数据
for(int i=0;i
r.V[i]=0; //直接访问保护数据
for(int j=0;j
}
return r;
}
这样一个乘法,由于直接访问矩阵类和向量类的保护数据,避免了频繁调用成员函数,效率就高多了。
需要友元的另一个原因是为了方便重载操作符的使用。18.2节和18.4节中有关于该内容的介绍。
友元函数不是成员函数,它是类的朋友,因而能够访问类的全部成员。在类的内部,只能声明它的函数原型,加上friend关键字。友元声明的位置可在类的任何部位,既可在public区,也可在protected区, 意义完全一样。友元函数定义则在类的外部,一般与类的成员函数定义放在一起。因为类重用时,一般友元是一起提供的。
一个类的成员函数可以是另一个类的友元。
例如,下面的代码中, 教师应该可以修改学生的成绩(访问学生类的保护数据),将教师类的成员函数assignGrades()声明为学生类的友元:
class Student; //前向声明类名声明
class Teacher
{
//...
public:
void assignGrade。(Student&。); //给定成绩
protected:
int noOfStudents;
Student'pList[100];
};
class Student
{
public:
//...
friend void Teacher::assignGrades(Student
protected:
Teacherx pT;
int semesterHours;
float gpa;
};
void Teacher::assignGrades(Student&s)
{
s.gpa=4.0;//修改学生的平均成绩gpa
}
在教师类声明中,声明了学生类对象的指针数组;在学生类声明中,又声明了教师类对象的指针和作为友元的教师类成员函数。解决这种交叉声明问题的方法是先进行类名声明。 如例中的“class Student;”让编译知道Student类的名字已经登记在册, 后面可以引用这个名字。类名声明不能用于定义该类的对象,因为这时还没有类的完整声明,没有分配类对象空间的依据(即不能在类名声明“classStudent;”后声明Student类之前,出现定义类对象语句:Student ss;)。
教师类中的成员函数assignGrades( )需要Student类的参数才可以访问参数(Student类对象)的保护数据。
整个类可以是另一个类的友元,该友元称为友类。友类的每个成员函数都可访问另一个类中的保护或私有数据成员。
例如,下面的代码将整个教师类看成是学生类的友类,教师既可修改成绩,又可调整学时数:
class Student;
class Teacher;
{
public:
void assignGrades(Student& s); //赋成绩
void adjustHours(Student& s); //调整学时数
//…
protected:
void noOfStudents;
Student* pList[100];
}
class Student
{
public:
friend class Teacher;//友类
float gpa;
};
文章出处:好像是某一个大学的网络资源,具体我不记得了,如果作者看见,请及时与我联系,我马上把文章出处详细标示出来.