少女祈祷中...

本文转载自:http://blog.pfan.cn/rickone/36219.html
我不知道关于C++关键字friend的全部议题有多少,我只对我了解的做个小结。

1,friend申明一个友元

friend一般为一句申明式,它位于一个类的内部,它申明一个类或者一个函数为该类的友元。friend并不是定义一个成员函数,所以 friend放在public,protected或者private前都可以,完全是一样的。做为一个友元,即表示在该类或者该函数内部可以访问这个类的私有成员,你和朋友之间是不是应该没有什么隐藏的呢。例子:

    class A
    {
    public:
     A(int _a) : a(_a) {}
     friend void test(A&);
     friend class B;
    private:
       int a;
    };
 
    void test(A& x)
    {
     x.a=100;//拥有私有成员a的访问权限
    }
 
    class B
    {
    public:
      void foo();
    };

如果friend申明式为一般的非模板类或者函数,则该处可以为首次申明。对于一个类,只能是申明式,对于函数,可以是它的定义式。

    class A
    {
    public:
     A(int _a) : a(_a) {}
     friend void test(A& x)
     {
        x.a = 100;//定义::test()友元函数
     }
     friend class B;
    private:
       int a;
    };

注意尽管将函数的定义式放在类内部,但它并不是一个成员函数,对于省略受限的定义形式它将成为一个全局函数::test(),当然你也可以申明另外一个类的成员函数为友元,如:

    class A
    {
    public:
     A(int _a) : a(_a) {}
     friend void B::foo();
    private:
       int a;
    };

总的来说,如果你想在哪里访问类A的私有成员,就在类A内写上一句该处的申明式,并在前面加上friend关键字。

这是一般情况,很简单,但是它会破坏封装的初衷,所以尽量少用;Effective C++中有一个应用的例子,对一个类定义的二元操作符,如果你希望它能对操作数都进行隐式转化,那么就定义一个全局函数,并申明成该类的友元。

2,模板函数作友元

先给一个模板函数,它是一个模板,并不是一个函数:

    template
    void foo1(T);

在定义foo1为某类的友元时,或者要实例化模板参数T,或者给出可演绎的申明式,而且就算是可以演绎的,一对尖括号也不能省。如:

    class A
    {
    public:
      friend void foo1(char);
      friend void foo1<>(double);
    };

或者给出限制符:::

    class A
    {
    public:
      friend void ::foo1(char);
    };

当然,如果有一般函数具有这种形式,那会优先于模板函数匹配。最后这里的申明式都不能是定义式,必须前至申明(定义)。

3,模板类里的友元

模板类里也能申明2中的友元,但是模板类有模板参数,如果利用了这个模板参数的友元申明,就属这种情形。

    template
    class A
    {
    public:
      friend void foo1(T);
    };

但是,在这里,必须要求foo1在这里是可见的,即不能是首次申明式。如果不使用模板参数,那会是一种有趣的情形。

    template
    class A
    {
    public:
      friend void foo(){}
    };

注意这里是一个定义式,它定义了一个::foo()函数为该模板类的友元,在该模板类具现的时候,::foo()也被具现出来,即:

    A a1;// ::foo()首次定义
    A a2;// ::foo()第二次定义,违背C++一次定义原则

4,友元模板

如果想定义一系列函数为该类的友元,可以使用友元模板。它和模板的申明式类似,只是在template<>后加了friend关键字。

    class A
    {
    public:
     template
     friend void foo();
    };

5,能否做为定义式

能做为定义式的情况是:非受限,没有前至::,没有模板参数列表,没一对尖括号。如果是模板申明式,不能是首次申明,在该处必须是可见的。

6,一个完整的例子

    template
    class Rat
    {
    public:
        Rat(T _a, T _b) : a(_a), b(_b) {}
        friend Rat operator*(Rat&amp;,Rat&amp;);
    private:
        T a,b;
    };
 
    template
    Rat operator*(Rat &amp; x, Rat &amp; y)
    {
        return Rat(x.a*y.a,x.b*y.b);
    }

Rat< T >为T类型的有理数类,定义它的相乘运算,定义一个全局函数,并申明为友元,该函数也应该是模板,希望有如上的程序通过编译。在friend式之前没有operator*()的申明,所以这里不能是首次申明,在前面必须加上申明式:

    template
    Rat operator*(Rat &amp; x, Rat &amp; y);

在这之前又没有Rat的申明,再加上:

    template
    class Rat;

通过编译,或者改成友元模板:

    template
    class Rat
    {
    public:
        Rat(T _a, T _b) : a(_a), b(_b) {}
        template
        friend Rat operator*(Rat&amp;,Rat&amp;);
    private:
        T a,b;
    };
 
    template
    Rat operator*(Rat &amp; x, Rat &amp; y)
    {
        return Rat(x.a*y.a,x.b*y.b);
    }

有细微的不同,Rat< T >申明了一系列友元operator*< UU >,当然没必要,只要 operator*< T >就够了,但是形式上简单一些。还有一种更简单的形式,就是将定义式放在里面,正是Effective C++里使用的方法:

    template
    class Rat
    {
    public:
        Rat(T _a, T _b) : a(_a), b(_b) {}
        friend Rat operator*(Rat&amp;x,Rat&amp;y) //定义并申明了::operator*()
        {
            return Rat(x.a*y.a,x.b*y.b);
        }
    private:
        T a,b;
    };

[完]

rickone 2008/06/20

再次感谢rickone带来详细的解释。

: http://www.deuxmille.org/archives/1248

本文相关评论 - 1条评论都没有呢

还没有评论呢。

  • :em14:
  • :em04:
  • :em11:
  • :em32:
  • :em08:
  • :em05:
  • :em17:
  • :em19:
  • :em24:
  • :em00:
  • :em31:
  • :em33:
  • :em06:
  • :em26:
  • :em27:
  • :em03:
  • :em28:
  • :em30:
  • :em13:
  • :em23:
  • :em21:
  • :em16:
  • :em20:
  • :em15:
  • :em07:
  • :em29:
  • :em02:
  • :em12:
  • :em18:
  • :em10:
  • :em01:
  • :em09:
  • :em22:
  • :em25:

Additional comments powered by BackType