友元
- 友元是一种允许非类成员函数访问类的非公有成员的一种机制。
-
可以把一个函数指定为类的友元,也可以把整个类指定为另一个类的友元。
-
友元函数
-
友元类
- 友元函数在类作用域外定义,但它需要在类体中进行说明。
- 为了与该类的成员函数加以区别,定义的方式是在类中用关键字friend说明该函数,格式如下:
friend 类型 友元函数名(参数表); -
友元的作用在于提高程序的运行效率。
下面用程序来使用下友元:
#include <iostream> using namespace std; class Point { friend double distance(const Point& p1, const Point& p2);//声明友员函数 public: Point(int x, int y); private: int x_; int y_; }; Point::Point(int x, int y):x_(x), y_(y) { } int main(void) { return 0; }
其中友员函数并不是类的成员函数,为了证明这点,我们先假设友员是成员函数,所以可以这样写:
编译一下:
友员的定义是在类体之外的,所以正确的实现友员函数的写法是去掉域访问修饰,如下:
既始在定义友元函数时在类体中实现实际上它也是在类体外:
下面来具体实现它,求两点之间的距离:
编译:
可见友元确实是可以访问类的私有变量,下面编写一下测试代码:
编译:
呃,这是啥子错,纠结了关天,最终发现是函数名的问题,可能是distance与系统函数重名了,所以需要修改一下,将首字母改为大写就OK了:
运行:
这就是友元使用的效果,在上面中说到“友元的作用在于提高程序的运行效率。”,何以见得呢?如果这个程序不用友元的话,则需要提供两个访问私有成员的函数,多了函数调用的开销。
- 友元函数不是类的成员函数,在函数体中访问对象的成员,必须用对象名加运算符“.”加对象成员名。但友元函数可以访问类中的所有成员(公有的、私有的、保护的),一般函数只能访问类中的公有成员。
- 友元函数不受类中的访问权限关键字限制,可以把它放在类的公有、私有、保护部分,但结果一样。
- 某类的友元函数的作用域并非该类作用域。如果该友元函数是另一类的成员函数,则其作用域为另一类的作用域,否则与一般函数相同。
- 友元函数破坏了面向对象程序设计类的封装性,所以友元函数如不是必须使用,则尽可能少用。或者用其他手段保证封装性。
- 如果某类B的成员函数会频繁的存取另一个类A的数据成员, 而A的数据成员的Private/Protectd限制造成B存取的麻烦, B只能通过A的Public的成员函数进行间接存取。
- 把B做成A类的友元类,即A类向B类开发其Private/Protectd内容, 让B直接存取。
- 友元类:一个类可以作另一个类的友元。
- 友元类的所有成员函数都是另一个类的友元函数。
- 友元类的声名:
friend class 类名;
下面来编写一个摇控器控制电视机的例子来说明友元类的使用:
#include <iostream> using namespace std; //电视机 class Television { public: Television(int volume, int chanel):volume_(volume),chanel_(chanel) { } private: int volume_;//音量 int chanel_;//频道 }; //摇控器 class TeleController { public: void volumeUp(Television& tv) { tv.volume_ += 1; } void volumeDown(Television& tv) { tv.volume_ -= 1; } void chanelUp(Television& tv) { tv.chanel_ += 1; } void chanelDown(Television& tv) { tv.chanel_ -= 1; } }; int main(void) { return 0; }
编译:
那友元类派上用场了:
#include <iostream> using namespace std; //电视机 class Television { friend class TeleController;//将摇控器类声明为电视机的友员类,这样它的私有成员对于摇控器而言就全公开了 public: Television(int volume, int chanel):volume_(volume),chanel_(chanel) { } private: int volume_;//音量 int chanel_;//频道 }; //摇控器 class TeleController { public: void volumeUp(Television& tv) { tv.volume_ += 1; } void volumeDown(Television& tv) { tv.volume_ -= 1; } void chanelUp(Television& tv) { tv.chanel_ += 1; } void chanelDown(Television& tv) { tv.chanel_ -= 1; } }; int main(void) { return 0; }
再次编译:
这时编写测试代码:
这里就不打印了,重在说明友元类的使用,电视机与遥控器类是写在一个cpp文件中,如果单独写在不同的cpp文件中会怎么呢?下面来看下:
Television.h:
#ifndef _TELEVISION_H_ #define _TELEVISION_H_ class Television { friend class TeleController; public: Television(int volume, int chanel); private: int volume_; int chanel_; }; #endif // _TELEVISION_H_
Television.cpp:
#include "Television.h" Television::Television(int volume, int chanel) : volume_(volume), chanel_(chanel) { }
TeleController.h:
#ifndef _TELE_CONTROLLER_H_ #define _TELE_CONTROLLER_H_ class TeleController { public: void VolumeUp(Television& tv); void VolumeDown(Television& tv); void ChanelUp(Television& tv); void ChanelDown(Television& tv); }; #endif // _TELE_CONTROLLER_H_
TeleController.cpp:
#include "TeleController.h" void TeleController::VolumeUp(Television& tv) { tv.volume_ += 1; } void TeleController::VolumeDown(Television& tv) { tv.volume_ -= 1; } void TeleController::ChanelUp(Television& tv) { tv.chanel_ += 1; } void TeleController::ChanelDown(Television& tv) { tv.volume_ -= 1; }
02.cpp:
#include <iostream> using namespace std; int main(void) { return 0; }
这时编译:
报错了,说明Television对于TeleController类在这种情况下是不可见的:
再次编译:
实际上遥控器头文件中的电视机引用也可以用前向声明来搞,修改代码如下:
再在具体实现中包含头文件既可:
修改之后再编译,依然好使,那这种前向声明它是有好外的:TeleController.h头文件会相对而言小一些,如果直接包含Television.h头文件,如果在多个文件中包含TeleController.h文件则文件会增大。
- 友元关系是单向的
A是B的友元类(在B类中有friend class A),并不代表B也是A的友元类。 - 友元关系不能被传递
A是B的友元类,B又是C的友元类,并不代表A是C的友元类。 - 友元关系不能被继承
A是B的友元类,C继承A,并不代表C是B的友元类。