友元

友元 英文 friendfriend 翻译成中文就是朋友,翻译成术语就是:友元。朋友就很好理解了,我的钱你随便花,我的东西你随便用;当然我也是你的朋友,你的钱我随便花,你的东西我随便用。

当然在 C++ 里,类与类 之间可以作为友元,那么这个类就可以去操作另外一个类里面私有的成员;函数与函数 之间也可以作为 友元,所以友元分为: 友元函数友元类

我们通过一个例子一看就明白了:

写代码

#include <iostream>
#include <string>

using namespace std;

class Screen{
public:
    typedef std::string::size_type index;

    Screen(int ht=0, int wd=0):contents(ht*wd, ' '), cursor(0), height(ht), width(wd){}

    int area() const{
        return height * width;
    }
private:
    std::string contents; 
    index cursor;
    int height, width;
};



int main(){
    Screen a;
    cout << a.area() << endl;
    cout << "OK" << endl;

    return 0;
}

运行现在的程序,程序是可以正常使用的。

现在,我们在main() 函数的同一级,编写一个函数,这个函数的功能是:计算屏幕(Screen)的面积。

// 这个函数不是类的成员函数
int calcArea(Screen & screen){
    return screen.height * screen.width;
}

如果这个函数像上面这个样子编写,程序编译都通不过。因为 heightwidth 都是 Screen 类的私有成员。这两个私有的数据成员只能在类的内部使用,只能被类的内部函数使用,因为这个calcArea() 函数不是类内部的成员函数,所以它是不能使用类内部的私有成员的。
那么,假如现在,我们把这个 calcArea() 函数变成这个 Screen 类的友元,就是朋友,那么这个函数就可以使用类里面私有的成员了。就像是:你是我的朋友,到我家里来,就像到自己家里一样,随便吃随便用随便拿。

现在我们就来定义友元函数。我们将calcArea() 函数定义成友元函数。

Screen 类里面,public里面添加下面这句代码就可以。就这么简单:

    friend int calcArea(Screen & screen);

现在,我们在编译程序,就没有错误了。运行也没有问题。main() 函数里面写成下面这个样子,来测试:

int main(){
    Screen a;
    cout << a.area() << endl;
    cout << calcArea(a) << endl;
    cout << "OK" << endl;
    while(1){}
    return 0;
}

我们也可以将一个类编写成友元,我们现在来看下面这个例子:(注意: 要将下面的这段代码放到Screen类的下面,否则Window_Mgr类找不到 Screen类。)

// 窗口管理类 - 对Screen类进行管理
class Window_Mgr{
public:
    // 重定位 - 就是改变窗口的height 和 width
    void relocate(int r, int c, Screen& s){
        s.height += r;
        s.width + c;
    }
};

因为 Screen 类里的 heightwidth 是私有的,不能再 Window_Mgr 类里面使用的,除非它是友元。
现在编译程序是报错的。
我们现在可以将Window_Mgr类作为Screen类的友元。需要做的事情,只需要:在Screen类里面的public里面添加下面一句代码:

    friend class Window_Mgr;

现在,我们将Window_Mgr 这个整个类都作为 Screen类的友元,也就是说:它们现在是朋友,我们家里的人可以随便到你们家里面来,你们家里的人也可以随便到我们家里面来。

我们在 main() 函数里面测试一下:

int main(){
    Screen a(60, 100);
    cout << a.area() << endl;

    Window_Mgr w;
    w.relocate(10, 20, a);
    cout << calcArea(a) << endl;

    cout << "OK" << endl;
    while(1){}
    return 0;
}

这就是友元,懂了吗?


如果我们现在不行让这个 Window_Mgr类作为 Screen类的友元,而是希望只是 Window_Mgr类中的一个成员函数是Screen类的友元。要如何操作呢?

我们在定义一个类,DogDog类里面就有很多的成员函数。

我现在不想将Dog类全部做成Screen类的友元,我只想将Dog类里面的foo()共有函数作为Screen类的友元,那现在我们要如何去做呢?
很简单,我们在Screen类的public里面添加下面这句代码就可以:

    friend int Dog::foo(Screen & );

Q: 现在程序编译时会通过的,这是为什么?


A: 有一个问题需要强调一下:友元定义 和 友元声明 之间有一个依赖。

如果我们将 Dog 类 放在 Screen 类的下面,那么 Dog 类是可以找到 Screen 类,但是 Screen 类却找不到 Dog 类里面的foo() 函数,因为我们在编写 foo() 函数的时候,是使用的声明和定义一体的形式编写的。
如果我们将 Dog 类 放在 Screen 类的上面, 那么 Screen 类是可以找到 Dog 类里面的 foo() 函数,但是 Dog 类现在又找不到 Screen 类了。所以,不管你将 Dog 类放在程序的什么位置,程序都是没有办法编译通过。要怎样解决这个问题呢?

我们需要在 Screen 类上面 这样写:

class Screen;

class Dog{
public:
    int foo(Screen & screen);

    int koo(Screen & screen){
        return 1;
    }
};

先对 Screen 类进行声明,接下定义 Dog 类。并且在 Dog 类里面 我们将 foo() 函数写成声明的形式。这样就没有使用到 Screen 类里面的成员,这样,程序在编译这段代码的时候不需要去找 Screen 的定义。


接着,我们在 Screen 类的下面 这样写:

int Dog::foo(Screen & screen){
    return  screen.height * screen.width;
}

我们在 Screen 类的下面都 Dog 类里面的成员函数 foo() 函数进行定义。这样做的原因是:因为 foo() 成员函数里面使用到了 Screen 类里面定义的成员变量,程序在编译这段函数的代码的时候,会去上向上找 Screen 类的定义,刚好我们将 foo() 函数的定义写在Screen 定义的下面,所以这样写就可以用个编译。

现在在main() 函数中写一些测试代码:

int main(){
    Screen a(60, 100);
    cout << a.area() << endl;

    Dog d;
    cout << d.foo(a) << endl;

    cout << "OK" << endl;
    while(1){}
    return 0;
}

现在 友元 我们就都介绍完毕了。

最后,完整的代码贴上:

#include <iostream>
#include <string>

using namespace std;

class Screen;

class Dog{
public:
    int foo(Screen & screen);

    int koo(Screen & screen){
        return 1;
    }
};


class Screen{
public:
    friend int calcArea(Screen & screen);
    friend class Window_Mgr;
    friend int Dog::foo(Screen & screen);

    typedef std::string::size_type index;

    Screen(int ht=0, int wd=0):contents(ht*wd, ' '), 
        cursor(0), height(ht), width(wd){}

    int area() const{
        return height * width;
    }

private:
    std::string contents; 
    index cursor;
    int height, width;
};

// 窗口管理类 - 对Screen类进行管理
class Window_Mgr{
public:
    // 重定位 - 就是改变窗口的height 和 width
    void relocate(int r, int c, Screen& s){
        s.height += r;
        s.width += c;
    }
};

int Dog::foo(Screen & screen){
    return  screen.height * screen.width;
}


// 这个函数不是类的成员函数
int calcArea(Screen & screen){
    return screen.height * screen.width;
}

int main(){
    Screen a(60, 100);
    cout << a.area() << endl;

    Window_Mgr w;
    w.relocate(10, 20, a);
    cout << calcArea(a) << endl;

    Dog d;
    cout << d.foo(a) << endl;

    cout << "OK" << endl;
    while(1){}
    return 0;
}


总结:
1 . 今天我们学习的就是友元,友元就是找朋友。我们可以将一个函数作为友元,我们也可以将一个类作为友元,也可以将类中的一个成员函数作为友元。
2 . 所有友元函数有两种: 普通函数 和 类的成员函数(共有 和 私有都可以。)
3 . 友元,道理上是很简单的。友元了之后,就可以操作所有私有的数据成员和私有函数。