C++之友元机制(友元函数和友元类)
我们已知道类具有封装和信息隐藏的特性。只有类的成员函数才能访问类的私有成员,程序中的其他函数是无法访问私有成员的。非成员函数可以访问类中的公有成员,但是如果将数据成员都定义为公有的,这又破坏了隐藏的特性。另外,应该看到在某些情况下,特别是在对某些成员函数多次调用时,由于参数传递,类型检查和安全性检查等都需要时间开销,而影响程序的运行效率。
为了解决上述问题,提出一种使用友元的方案。友元是一种定义在类外部的普通函数,但它需要在类体内进行说明,为了与该类的成员函数加以区别,在说明时前面加以关键字friend。友元不是成员函数,但是它可以访问类中的私有成员。友元的作用在于提高程序的运行效率(即减少了类型检查和安全性检查等都需要的时间开销),但是,它破坏了类的封装性和隐藏性,使得非成员函数可以访问类的私有成员。
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方法来实现成员变量的存取,在这种情况下,不妨用友元的方法。
///
/// @file Complex.cc
/// @author AlexCthon(AlexCthon@163.com)
/// @date 2018-06-12 10:10:03
/// #include <iostream>
using std::cout;
using std::endl; class Complex
{
public:
Complex(double dreal = 0, double dimag = 0)
: _dreal(dreal)
, _dimag(dimag)
{} void display() {
cout << _dreal << " + " << _dimag << "i" << endl;
} //成员函数的形式
Complex operator+(const Complex & rhs)
{
return Complex(_dreal + rhs._dreal, _dimag + rhs._dimag);
} private:
double _dreal;
double _dimag;
}; int main(void)
{
Complex c1(1, 2);
Complex c2(3, 4); Complex c3 = c1 + c2;
c3.display(); Complex c4 = c1 + 5;// c1.operator+(5);
c4.display(); Complex c5 = 5 + c1;//operator+(5, c1);
c5.display(); return 0;
}
有关运算符重载更多细节,参考这篇博客:https://www.cnblogs.com/cthon/p/9181404.html
C++之友元机制(友元函数和友元类)的更多相关文章
- C++ 友元 (全局函数做友元) (类做友元) (成员函数做友元)
1 //友元 全局函数做友元 2 /* 3 #include <iostream> 4 #include <string> 5 using namespace std; 6 7 ...
- C++复习:函数模板和类模板
前言 C++提供了函数模板(function template).所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表.这个通用函数就称为函数模板.凡是函数体 ...
- 友元(友元函数、友元类和友元成员函数) C++
有些情况下,允许特定的非成员函数访问一个类的私有成员,同时仍阻止一般的访问,这是很方便做到的.例如被重载的操作符,如输入或输出操作符,经常需要访问类的私有数据成员. 友元(frend)机制允许一个类将 ...
- C++友元(友元函数、友元类和友元成员函数)
友元(友元函数.友元类和友元成员函数) C++ 有些情况下,允许特定的非成员函数访问一个类的私有成员,同时仍阻止一般的访问,这是很方便做到的.例如被重载的操作符,如输入或输出操作符,经常需要访问类的私 ...
- C++类的友元机制说明
下面给出C++类的友元机制说明(对类private.protected成员访问),需要注意的是,友元机制尽量不用或者少用,虽然它会提供某种程度的效率,但会带来数据安全性的问题. 类的友元 友元是C++ ...
- C++学习12 友元函数和友元类
友元函数和友元类在实际开发中较少使用,想快速学习C++的读者可以跳过本节. 一个类中可以有 public.protected.private 三种属性的成员,通过对象可以访问 public 成员,只有 ...
- C++:成员运算符重载函数和友元运算符重载函数的比较
5.2.4 成员运算符重载函数和友元运算符重载函数的比较 (1)对双目运算符而言,成员运算符重载函数参数表中含有一个参数,而友元运算符重载函数参数表中有两个参数:对于单目运算符而言,成员运算符重载函数 ...
- C++:运算符重载函数之友元运算符重载
5.2.2 友元运算符重载函数 运算符重载函数一般采用两种形式定义: 一是定义为它将要操作的类的成员函数(简称运算符重载函数): 二是定义为类的友元函数(简称为友元运算符重载函数). 1.定义友元运算 ...
- C++:友元(非成员友元函数、成员友元函数、友元类)
3.8 友元:友元函数和友元类 友元函数 :既可以是不属于任何类的非成员函数,也可以是另一个类的成员函数,统称为友元函数.友元函数不是当前类的成员函数,而是独立于类的外部函数,但它可以访问该类所有的 ...
随机推荐
- 微信公众平台SDK for node
实现了下面特性: 1.开启开发人员模式 2.解析微信请求參数 3.验证消息来源 4.被动回复文字消息 5.被动回复图文消息 6.获取access_token 7.创建自己定义菜单 地址:wechat ...
- centos7+ 安装Docker 17.03.2
cnetos7 安装 docker17.03.2 升级内核 http://m.blog.csdn.net/article/details?id=52047780 注意切换内核时查看 新内核位置 awk ...
- nightwatch.js - scroll until element is visible
.getLocationInView() Determine an element's location on the screen once it has been scrolled into vi ...
- hibernate5(10)注解映射[2]一对多单向关联
在上一篇文章里.我们从端方向一端建立关联关系,完毕了从文章到作者的关联关系建立.但在实际的博客站点中,用户肯定还须要获取自己所写的文章,这时能够建立用户(一)对文章(多)的单向关联映射. 先来看我们的 ...
- 【每日Scrum】第八天(4.29) TD学生助手Sprint2
站立会议 组员 今天 签到 刘铸辉 (组长) 绩效考核 Y 刘静 测试用例书写 测试bug报告 测试详细报告 Y 解凤娇 Y 王洪叶 项目可行性报告 项目开发计划书 需求分析(已完成并发布) Y 胡宝 ...
- UVA 1541 - To Bet or Not To Bet(概率递推)
UVA 1541 - To Bet or Not To Bet 题目链接 题意:这题题意真是神了- -.看半天,大概是玩一个游戏,開始在位置0.终点在位置m + 1,每次扔一个硬币,正面走一步,反面走 ...
- Spark技术内幕: Task向Executor提交的源代码解析
在上文<Spark技术内幕:Stage划分及提交源代码分析>中,我们分析了Stage的生成和提交.可是Stage的提交,仅仅是DAGScheduler完毕了对DAG的划分,生成了一个计算拓 ...
- android android:duplicateParentState="true" "false"
今天要做一个效果.组件RelativeLayout上有两个TextView.这两个TextView具有不同的颜色值,如今要的效果是,当RelativeLayout被点击时,整个item有高亮背景. 同 ...
- ffmpeg强制使用TCP方式推流到EasyDarwin开源流媒体服务器进行直播
我们的EasyDarwin目前部署在阿里云的服务器上面,运行的效果是非常好的,而且无论是以TCP方式.还是UDP的方式推送,都可以非常好地进行直播转发: 但并不是所有的用户服务器都是阿里云的形式,有很 ...
- 九度OJ 1139:最大子矩阵 (矩阵运算、缓存)
时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:1014 解决:376 题目描述: 已知矩阵的大小定义为矩阵中所有元素的和.给定一个矩阵,你的任务是找到最大的非空(大小至少是1 * 1)子矩 ...