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 友元:友元函数和友元类 友元函数 :既可以是不属于任何类的非成员函数,也可以是另一个类的成员函数,统称为友元函数.友元函数不是当前类的成员函数,而是独立于类的外部函数,但它可以访问该类所有的 ...
随机推荐
- 几种支持动作模型格式的比较(MD2,MD5,sea3d) 【转】
最近使用了几种不同的模型格式做人物动作的表现,记录一下优缺点 1) MD2 数据内容: 记录了所有动作顶点数据 数据格式: 二进制 动作文件: 动作文件合并在一个模型文件 文件大小: 动作多时很大 ...
- C# PropertyGrid控件应用心得 【转】
源文 : http://blog.csdn.net/luyifeiniu/article/details/5426960 c#stringattributesobjectmicrosoftclass ...
- 安装 - LNMP一键安装包
https://lnmp.org/ 系统需求: CentOS/RHEL/Fedora/Debian/Ubuntu/Raspbian Linux系统 需要5GB以上硬盘剩余空间 需要128MB以上内存( ...
- 《图论》——广度优先遍历算法(BFS)
十大算法之广度优先遍历: 本文以实例形式讲述了基于Java的图的广度优先遍历算法实现方法,详细方法例如以下: 用邻接矩阵存储图方法: 1.确定图的顶点个数和边的个数 2.输入顶点信息存储在一维数组ve ...
- 【西祠日志】【07】努力努力,找资料,思考,怎么做asp图片上传
[西祠日志][07]努力努力,找资料.思考.怎么做asp图片上传 (2015.07.23周四) 今天忘了带本子.直接写在书上了笔记,晚点还是夹在本子里. 学了这么久的web应用,一直都没时间去做一点 ...
- Flex4_Tree组件2(添加多选框、修改树图标)
1.新建AS类,用于为Tree生成复选框,及一些选择事件. package com.th.myUtils { import flash.events.Event; import flash.event ...
- python(18)- 协程函数及应用
协程 def init(func): def wrapper(*args,**kwargs): obj = func(*args,**kwargs) next(obj) return obj retu ...
- C# Select SelectMany 区别
string[] text = { "Today is 2018-06-06", "weather is sunny", "I am happy&qu ...
- hdu 1081 & poj 1050 To The Max(最大和的子矩阵)
转载请注明出处:http://blog.csdn.net/u012860063 Description Given a two-dimensional array of positive and ne ...
- sql quer
SELECT (SELECT COUNT (sysid) FROM FwInvConsumable WHERE parentref = g.sysid AND (ns.state = 'Invento ...