1. 友元函数的简单介绍
1.1 为什么要使用友元函数
在实现类之间数据共享时, 减少系统开销, 提高效率. 如果类 A 中的函数要访问类 B 中的成员 (例如: 智能指针类的实现), 那么类 A 中该函数要是类 B 的友元函数. 具体来说: 为了使其他类的成员函数直接访问该类的私有变量. 即: 允许外面的类或函数去访问类的私有变量和保护变量, 从而使两个类共享同一函数.
实际上具体大概有下面两种情况需要使用友元函数:(1) 运算符重载的某些场合需要使用友元.(2) 两个类要共享数据的时候.
1.2 使用友元函数的优缺点
优点: 能够提高效率, 表达简单, 清晰.
缺点: 友元函数破环了封装机制, 尽量不使用成员函数, 除非不得已的情况下才使用友元函数.
2. 友元函数的使用
2.1 友元函数的参数:
因为友元函数没有 this 指针, 则参数要有三种情况:
(1) 要访问非 static 成员时, 需要对象做参数;
(2) 要访问 static 成员或全局变量时, 则不需要对象做参数;
(3) 如果做参数的对象是全局对象, 则不需要对象做参数;
2.2 友元函数的位置
因为友元函数是类外的函数, 所以它的声明可以放在类的私有段或公有段且没有区别.
2.3 友元函数的调用
可以直接调用友元函数, 不需要通过对象或指针
2.4 友元函数的分类:
根据这个函数的来源不同, 可以分为三种方法:
普通函数友元函数
目的: 使普通函数能够访问类的友元
语法:
声明: friend + 普通函数声明
实现位置: 可以在类外或类中
实现代码: 与普通函数相同
调用: 类似普通函数, 直接调用
- ///
- /// @file Point.cc
- /// @author AlexCthon(AlexCthon@163.com)
- /// @date 2018-06-12 09:40:14
- ///
- #include <math.h>
- #include <iostream>
- using std::cout;
- using std::endl;
- class Point
- {
- public:
- Point(int ix = 0, int iy = 0)
- : _ix(ix)
- , _iy(iy)
- {
- cout <<"Point(int=0, int=0)" << endl;
- }
- void print() const
- {
- cout << "(" << _ix
- << "," << _iy
- << ")" << endl;
- }
- int getX() const { return _ix; }
- int getY() const { return _iy; }
- // 友元之普通函数
- friend float distance(const Point & lhs, const Point & rhs);
- private:
- int _ix;
- int _iy;
- };
- #if 0
- float distance(const Point & lhs, const Point & rhs)
- {
- return sqrt((lhs.getX() - rhs.getX()) * (lhs.getX() - rhs.getX()) +
- (lhs.getY() - rhs.getY()) * (lhs.getY() - rhs.getY()));
- }
- #endif
- float distance(const Point & lhs, const Point & rhs)
- {
- return sqrt((lhs._ix - rhs._ix) * (lhs._ix - rhs._ix) + (lhs._iy - rhs._iy) * (lhs._iy - rhs._iy));
- }
- int main(void)
- {
- Point pt1(1, 2);
- Point pt2(3, 4);
- cout << "pt1 和 pt2 之间的距离:" << distance(pt1, pt2) << endl;
- return 0;
- }
类 Y 的一个成员函数为类 X 的友元函数
目的: 使类 Y 的一个成员函数成为类 X 的友元, 具体而言: 在类 Y 的这个成员函数中, 借助参数 X, 可以直接以 X 的私有变量
语法:
声明位置: 声明在公有中 (本身为函数)
声明: friend + 成员函数的声明
调用: 先定义 Y 的对象 y--- 使用 y 调用自己的成员函数 --- 自己的成员函数中使用了友元机制
代码:
- #include <math.h>
- #include <iostream>
- using std::cout;
- using std::endl;
- class Point;// 类的前向声明
- class Line
- {
- public:
- float distance(const Point & lhs, const Point & rhs);
- };
- class Point
- {
- public:
- Point(int ix = 0, int iy = 0)
- : _ix(ix)
- , _iy(iy)
- {
- cout <<"Point(int=0, int=0)" << endl;
- }
- void print() const
- {
- cout << "(" << _ix
- << "," << _iy
- << ")" << endl;
- }
- // 友元之成员函数
- friend float Line::distance(const Point & lhs, const Point & rhs);
- private:
- int _ix;
- int _iy;
- };
- #if 0
- float distance(const Point & lhs, const Point & rhs)
- {
- return sqrt((lhs.getX() - rhs.getX()) * (lhs.getX() - rhs.getX()) +
- (lhs.getY() - rhs.getY()) * (lhs.getY() - rhs.getY()));
- }
- #endif
- float Line::distance(const Point & lhs, const Point & rhs)
- {
- return sqrt((lhs._ix - rhs._ix) * (lhs._ix - rhs._ix) +
- (lhs._iy - rhs._iy) * (lhs._iy - rhs._iy));
- }
- int main(void)
- {
- Point pt1(1, 2);
- Point pt2(3, 4);
- Line line;
- cout << "pt1 和 pt2 之间的距离:" << line.distance(pt1, pt2) << endl;
- return 0;
- }
类 Y 的所有成员函数都为类 X 友元函数 - 友元类
目的: 使用单个声明使 Y 类的所有函数成为类 X 的友元, 它提供一种类之间合作的一种方式, 使类 Y 的对象可以具有类 X 和类 Y 的功能.
语法:
声明位置: 公有私有均可, 常写为私有 (把类看成一个变量)
声明: friend + 类名 (不是对象)
补充: 当用到友元成员函数时, 需注意友元声明与友元定义之间的互相依赖. 类的前置声明.
- #include <math.h>
- #include <iostream>
- using std::cout;
- using std::endl;
- class Point;// 类的前向声明
- class Line
- {
- public:
- float distance(const Point & lhs, const Point & rhs);
- void setPoint(int ix, int iy, Point & pt);
- private:
- int _iz;
- };
- class Point
- {
- public:
- Point(int ix = 0, int iy = 0)
- : _ix(ix)
- , _iy(iy)
- {
- cout <<"Point(int=0, int=0)" << endl;
- }
- void print() const
- {
- cout << "(" << _ix
- << "," << _iy
- << ")" << endl;
- }
- // 友元之友元类
- //friend class Line;
- friend Line;// 一定是破坏了类的封装性
- // 友元是单向的, 不具备传递性, 不能继承
- //
- //A -> B, B -> C ==> A -> C
- void setZ(Line & line, int iz)
- {//Point 不能访问 Line 的私有成员
- line._iz = iz;
- }
- private:
- int _ix;
- int _iy;
- };
- float Line::distance(const Point & lhs, const Point & rhs)
- {
- return sqrt((lhs._ix - rhs._ix) * (lhs._ix - rhs._ix) +
- (lhs._iy - rhs._iy) * (lhs._iy - rhs._iy));
- }
- void Line::setPoint(int ix, int iy, Point & pt)
- {
- pt._ix = ix;
- pt._iy = iy;
- }
- int main(void)
- {
- Point pt1(1, 2);
- Point pt2(3, 4);
- Line line;
- cout <<"pt1 和 pt2 之间的距离:" << line.distance(pt1, pt2) << endl;
- line.setPoint(5, 6, pt1);
- cout << "pt1 =";
- pt1.print();
- return 0;
- }
小结: 其实一些操作符的重载实现也是要在类外实现的, 那么通常这样的话, 声明为类的友元是必须滴.
4. 友元函数和类的成员函数的区别
友元类的所有成员函数都是另一个类的友元函数, 都可以访问另一个类中的隐藏信息 (包括私有成员和保护成员). 当希望一个类可以存取另一个类的私有成员时, 可以将该类声明为另一类的友元类.
成员函数有 this 指针, 而友元函数没有 this 指针. 这点其实和静态成员函数一样, 静态成员函数也是没有 this 指针的, 所以它只能访问静态成员变量或者通过对象访问非静态成员变量.
友元类是单向的, 不可传递, 不能被继承.
- class Rect
- {
- public:
- Rect() // 构造函数, 计数器加 1
- {
- count++;
- }
- //Rect(const Rect& r)
- //{
- // width = r.width;
- // height = r.height;
- // count++;
- //}
- ~Rect() // 析构函数, 计数器减 1
- {
- count--;
- }
- static int getCount() // 返回计数器的值
- {
- return count;
- }
- friend int get();
- private:
- int width;
- int height;
- static int count; // 一静态成员做为计数器
- };
- int Rect::count = 0; // 初始化计数器 , 静态成员变量必须要在类外部初始化
- int get()
- {
- return Rect::count;// 友元函数通过类访问私有静态成员变量
- }
- int main()
- {
- Rect rect1;
- cout<<"The count of Rect:"<<Rect::getCount()<<endl;// 通过类访问公有静态成员函数, 输出 1
- Rect rect2(rect1); // 使用 rect1 复制 rect2, 此时应该有两个对象
- cout<<"The count of Rect:"<<Rect::getCount()<<endl; // 输出 1
- cout << get() << endl;// 输出 1
- //cout << Rect::count << endl;// 不能编译通过, 不能访问私有成员. this 只能访问类中的非静态成员变量或成员函数
- system("pause");
- return 0;
- }
注意: 怎么理解友元类是单向的, 不可传递, 不能被继承?
1, 友元类不能被继承. 这个不多说, 很容易理解.
2, 友元类是单向的. A->B,B->A? 这是错误的.
3, 友元类是不可传递的. A->B,B->C, A=>C? 这是错误的.
补充: 当类的成员变量很多时, 需要提供许多的 get/set 方法来实现成员变量的存取, 在这种情况下, 不妨用友元的方法.
来源: http://www.bubuko.com/infodetail-2643541.html