C++基础--友元

来源:互联网 发布:路基压实度灌砂法软件 编辑:程序博客网 时间:2024/05/16 06:59
C++基础--友元
我们知道:类的公有成员能够在类外访问,私有的成员只能被类的其它成员函数访问。在C++中,可以定义友元,如果某一个函数定义为类的友元,则该函数就可以访问该类的私有成员,下面举例说明友元函数的使用。

假定我们已经定义了两个集合类变量,一个为整型数集合,另一个为实型数集合:
程序段程序段10-7 

class IntSet {
public: 
 //...
private:
 int elems[maxCard];
 int card;
};

class RealSet {
public: 
 //...
private:
 float elems[maxCard];
 int card;
};

  我们还要定义一个函数SetToReal,其功能是将整型数集合转换为实型数集合。SetToReal可以定义为IntSet类的成员函数。程序段程序段10-8 

void IntSet::SetToReal (RealSet &set)
{
 set.EmptySet();
 for (register i = 0; i < card; ++i)
  set.AddElem((float) elems[i]);
}

  这个函数中,整型数集合中的每一个元素作为实参,调用实型数集合中的AddElem成员函数的开销较大。如果我们将SetToReal函数定义为RealSet类的友元,使得SetToReal函数能够访问RealSet类的私有成员,则可提高程序运行的效率。程序段程序段10-9 

class RealSet {
 //...
 friend void IntSet::SetToReal (RealSet&);
};

void IntSet::SetToReal (RealSet &set)
{
 set.card = card;
 for (register i = 0; i < card; ++i)
  set.elems[i] = (float) elems[i];
}

  从上面我们可以看出:定义友元需用friend关键字,SetToReal函数定义为类RealSet的友元后,就能够直接访问RealSet类的私有数据成员elems,避免了函数调用的开销,从而提高了程序运行的效率。
其实,我们也可以把一个类定义为另一个类的友元,例如: 
  class A;
  class B {
   //...
   friend class A;
  }; 
  类A是类B的友元,类A中的所有成员函数都能访问类B的私有成员。
  全局函数也可以作为类的友元。例如,假定我们将SetToReal定义为全局函数,并定义为IntSet和RealSet两个类的友元:
程序段程序段10-10 

class IntSet {
 //...
 friend void SetToReal (IntSet&, RealSet&);
};

class RealSet {
 //...
 friend void SetToReal (IntSet&, RealSet&);
};

void SetToReal (IntSet &iSet, RealSet &rSet)
{
 rSet.card = iSet.card;
 for (int i = 0; i < iSet.card; ++i)
  rSet.elems[i] = (float) iSet.elems[i];
}

  注意:虽然友元定义出现在类内,但它并不是类的成员。友元在类内定义的位置也是任意的,不管是出现在类的private、pretected或public部分,含义均相同。
  虽然友元为我们进行程序设计提供了一定的方便性,但是面向对象的程序设计要求类的接口与类的实现分开,对对象的访问通过其接口函数进行。如果直接访问对象的私有成员,就破坏了面向对象程序的信息隐藏和封装特性,虽然提供了一些方便,但有可能是得不偿失的,所以,我们要慎用友元。

C++的存取控制
  C++有三个关键字,用于设置类成员的访问权限,它们是:public、private和protected。
  ◇ public意味着在其后声明的所有成员在类外可以访问;
  ◇ private关键字则意味着,除了该类的成员函数之外,类外不能访问这些成员。如果试图访问私有成员,就会产生编译错误。类中缺省访问权限说明的的成员声明是private的;
  ◇ protected和private区别在于:protected成员可以被派生类访问,而private成员则不能被派生类访问。
友元的使用
  类的私有成员只能在类内访问,那么有没有可能让类外的成员访问?其实,在C++中,友元可以访问类的私有成员。友元不是类的成员,但必须在类的内部声明。因为"谁是我的朋友"必须由"我自己决定"。绝对不能允许下面的现象发生:李四说,"嘿,我是张三的朋友",然后李四就开始使用张三的一切,包括它的私有财产。李四究竟是不是张三的朋友,必须由张三自己说了算。
  通过friend关键字,可以把一个全局函数声明为友元,也可以把另一个类中的成员函数,甚至整个类都声明为友元,请看下面的例子:
例题例10-25 

class X; //Declaration
class Y
{
 void f(X*);
};
class X
{
private:
 int i;
public:
 void initialize();
 friend void g(X*, int); //Global friend
 friend void Y::f(X*); //class member friend
 friend class Z; //Entire class is a friend
 friend void h(); 
};
void X::initialize()
{
 i = 0;
}
void g(X* x, int i)
{
 x->i = i;
}
void Y::f(X* x)
{
 x->i = 100;
}
class Z
{
private :
 int j;
public:
 void initialize();
 void g(X* x);
};
void Z::initialize()
{
 j = 99;
}
void Z::g(X* x)
{
 x->i += j;
}
void h()
{
 X x;
 x.i = 200; //直接数据操作
}
void main()
{
 X x;
 Z z;
 z.g(&x);
}

  类Y有一个成员函数f(),它要访问X对象的私有数据成员i,需将Y::f(X*)声明为类X的友元。这里有一个难题,因为C++的编译器要求在使用任一变量之前必须声明,所以,类Y必须在它的成员Y::f(X*)被声明为类X的一个友元之前声明。但声明Y::f(X*)之前,又必须声明类X。
  解决的办法是:首先声明类X,但是这个类X的声明并不完整,能行吗?在本例中是可以的,这是由于Y::f(X*)的参数是指向X对象的指针,所以编译器允许在声明Y::f(X*)之前做一个不完全的类型指定。仅仅是告诉编译器,有一个叫X的类。这样,在类X中,Y::f(X*)就可以成功地声明为一个友元函数。
  再来看看其它两个友元函数,第一个声明将一个全局函数g()作为一个友元,但g()在这之前并没有定义,这表明friend可以同时声明全局函数和友元。同样,friend可以同时声明类Z及类Z作为类Y的友元。

嵌套友元
  对于嵌套类,也不能访问私有成员。嵌套类要访问私有成员也必须定义为友元。请看下面的例子:
例题例10-26 

#define SZ 20
class holder
{
private:
  int a[SZ];
 public:
  void initialize();
  class pointer
  {
  private:
   holder *h;
   int* p;
  public:
   void initialize(holder* H);
   //move around in the array.
   void next();
   void previous();
   void top();
   void end();
   //access values.
   int read();
   void set(int i);
  };
  friend holder::pointer;
};

  类holder包含一个整型数组和一个嵌套类pointer。通过定义pointer为holder的友元,使得pointer成员可以访问holder类的私有成员:
  friend holder : :pointer ;

关于友元的使用
  如果一个函数被某一个类声明为friend,就意味着它不是这个类的成员函数,但却可以访问该类的私有成员,而且它必须被列在类的定义中。虽然友元这种机制为我们进行程序设计提供了一定的方便性,但是,面向对象的程序设计要求类的接口与类的实现分开,对对象的访问通过其接口函数进行。如果直接访问对象的私有成员,就破坏了面向对象程序的信息隐藏和封装特性,虽然提供了一些方便,但有可能是得不偿失的,所以,我们要慎用友元。 

原创粉丝点击