1.友元函数的简单介绍
1.1为什么要使用友元函数
在实现类之间数据共享时,减少系统开销,提高效率。如果类A中的函数要访问类B中的成员(例如:智能指针类的实现),那么类A中该函数要是类B的友元函数。具体来说:为了
使其他类的成员函数直接访问该类的私有变量。即:允许外面的类或函数去访问类的私有变量和保护变量,从而使两个类共享同一函数。
实际上具体大概有下面两种情况需要使用友元函数:(1)运算符重载的某些场合需要使用友元。(2)两个类要共享数据的时候。
1.2使用友元函数的优缺点
1.2.1优点:能够提高效率,表达简单、清晰。
1.2.2缺点:友元函数破环了封装机制,尽量不使用成员函数,除非不得已的情况下才使用友元函数。
2.友元函数的使用
2.1友元函数的参数:
因为友元函数没有this指针,则参数要有三种情况:
2.1.1 要访问非static成员时,需要对象做参数;
2.1.2 要访问static成员或全局变量时,则不需要对象做参数;
2.1.3 如果做参数的对象是全局对象,则不需要对象做参数;
2.2友元函数的位置
因为友元函数是类外的函数,所以它的声明可以放在类的私有段或公有段且没有区别。
2.3友元函数的调用
可以直接调用友元函数,不需要通过对象或指针
2.4友元函数的分类:
根据这个函数的来源不同,可以分为三种方法:
2.4.1普通函数友元函数
2.4.1.1 目的:使普通函数能够访问类的友元
2.4.1.2 语法:
声明: friend + 普通函数声明
实现位置:可以在类外或类中
实现代码:与普通函数相同
调用:类似普通函数,直接调用
2.4.1.3代码:
class INTEGER
{
friend void Print(const INTEGER& obj);//声明友元函数
};
void Print(const INTEGER& obj)
{
//函数体
}
void main()
{
INTEGER obj;
Print(obj);//直接调用
}
2.4.2类Y的所有成员函数都为类X友元函数—友元类
2.4.2.1目的:使用单个声明使Y类的所有函数成为类X的友元,它提供一种类之间合作的一种方式,使类Y的对象可以具有类X和类Y的功能。
2.4.2.2语法:
声明位置:公有私有均可,常写为私有(把类看成一个变量)
声明: friend + 类名(不是对象哦)
2.4.2.3代码:
class girl;
class boy
{
public:
void disp(girl &);
};
void boy::disp(girl &x) //函数disp()为类boy的成员函数,也是类girl的友元函数
{
cout<<"girl's name is:"<
}
class girl
{
private:
char *name;
int age;
friend boy; //声明类boy是类girl的友元
};
main函数就不写了和普通调用时一样的。
2.4.3类Y的一个成员函数为类X的友元函数
2.4.3.1目的:使类Y的一个成员函数成为类X的友元,具体而言:在类Y的这个成员函数中,借助参数X,可以直接以X的私有变量
2.4.3.2语法:
声明位置:声明在公有中 (本身为函数)
声明:friend + 成员函数的声明
调用:先定义Y的对象y---使用y调用自己的成员函数---自己的成员函数中使用了友元机制
2.4.3.3代码:
实现代码和2.4.2.3中的实现及其相似只是设置友元的时候变为friend void boy::disp(girl &);自己解决喽……
小结:其实一些操作符的重载实现也是要在类外实现的,那么通常这样的话,声明为类的友元是必须滴。
4.友元函数和类的成员函数的区别
4.1 成员函数有this指针,而友元函数没有this指针。
4.2 友元函数是不能被继承的,就像父亲的朋友未必是儿子的朋友。
运算符重载定义:
C++中预定义的运算符的操作对象只能是基本数据类型。但实际上,对于许多用户自定义类型(例如类),也需要类似的运算操作。这时就必须在C++中重新定义这些运算符,赋予已有运算符新的功能,使它能够用于特定类型执行特定的操作。运算符重载的实质是函数重载,它提供了C++的可扩展性,也是C++最吸引人的特性之一。
运算符重载是通过创建运算符函数实现的,运算符函数定义了重载的运算符将要进行的操作。运算符函数的定义与其他函数的定义类似,惟一的区别是运算符函数的函数名是由关键字operator和其后要重载的运算符符号构成的。运算符函数定义的一般格式如下:
<返回类型说明符> operator <运算符符号>(<参数表>)
{
<函数体>
}
2.运算符重载时要遵循以下规则:
1)运算符重载函数也是函数,重载的运算符不会改变运算符的优先级、结合型和参数的个数。
2)重载运算符不能违反语言的语法规则。
3)赋值运算符除外,重载运算符可由派生类继承下去。
4)重载运算符不能使用默认参数。
5)运算符=、()、[]和->等操作符必须定义为类的成员函数,将这些操作符定义为友元函数将在编译时标记为错误。
6)C++中不能重载的运算符只有5个:
. (成员访问运算符)
.*(成员指针访问运算符)
∷ (域运算符)
sizeof(长度运算符)
?:(条件运算符)
因为前两个运算符不能重载是为了保证访问成员的功能不能被改变,域运算符和sizeof运算符的运算对象是类型而不是变量或一般表达式,不具重载的特征。
7)友元运算符的参数规则与类成员运算符的参数规则不同,一元运算符必须显示地声明一个参数,二元运算符必须显示地声明两个参数。类成员运算符重载时,参数中隐含了一个this指针。(另外,C++规定,有的运算符(如赋值运算符、下标运算符、函数调用运算符)必须定义为类的成员函数,有的运算符则不能定义为类的成员函数(如流输入“>>”和流输出“<<”运算符、类型转换运算符))。
重载为类的成员函数时,参数个数=原操作数个数-1(后置++、--除外),它可以通过this指针自由地访问本类的数据成员,可以少写一个函数的参数,但必须要求运算表达式的第一个参数(即运算符左侧的操作数)是一个类对象。
重载为类的友元函数时,参数个数=原操作数个数,且至少应该有一个自定义类型的形参。
8) 重载运算符限制在C++语言中已有的运算符范围内的允许重载的运算符之中,不能创建新的运算符。
9) 运算符重载实质上是函数重载,因此编译程序对运算符重载的选择,遵循函数重载的选择原则。
10) 重载之后的运算符不能改变运算符的优先级和结合性,也不能改变运算符操作数的个数及语法结构。
3.运算符重载的形式:
运算符函数重载一般有两种形式:重载为类的成员函数和重载为类的非成员函数。非成员函数通常是友元。(可以把一个运算符作为一个非成员、非友元函数重载。但是,这样的运算符函数访问类的私有和保护成员时,必须使用类的公有接口中提供的设置数据和读取数据的函数,调用这些函数时会降低性能。可以内联这些函数以提高性能。)
1) 成员函数运算符
运算符重载为类的成员函数的一般格式为:
<函数类型> operator <运算符>(<参数表>)
{
<函数体>
}
当运算符重载为类的成员函数时,函数的参数个数比原来的操作数要少一个(后置单目运算符除外),这是因为成员函数用this指针隐式地访问了类的一个对象,它充当了运算符函数最左边的操作数。因此:
(1) 双目运算符重载为类的成员函数时,函数只显式说明一个参数,该形参是运算符的右操作数。
(2) 前置单目运算符重载为类的成员函数时,不需要显式说明参数,即函数没有形参。
(3) 后置单目运算符重载为类的成员函数时,函数要带有一个整型形参。
调用成员函数运算符的格式如下:
<对象名>.operator <运算符>(<参数>)
它等价于
<对象名><运算符><参数>
例如:a+b等价于a.operator +(b)。一般情况下,我们采用运算符的习惯表达方式。
2) 友元函数运算符
运算符重载为类的友元函数的一般格式为:
friend <函数类型> operator <运算符>(<参数表>)
{
<函数体>
}
当运算符重载为类的友元函数时,由于没有隐含的this指针,因此操作数的个数没有变化,所有的操作数都必须通过函数的形参进行传递,函数的参数与操作数自左至右一一对应。
调用友元函数运算符的格式如下:
operator <运算符>(<参数1>,<参数2>)
它等价于
<参数1><运算符><参数2>
例如:a+b等价于operator +(a,b)。
4.两种重载形式的比较
在多数情况下,将运算符重载为类的成员函数和类的友元函数都是可以的。但成员函数运算符与友元函数运算符也具有各自的一些特点:
(1) 一般情况下,单目运算符最好重载为类的成员函数;双目运算符则最好重载为类的友元函数。
(2) 以下一些双目运算符不能重载为类的友元函数:=、()、[]、->。
(3) 类型转换函数只能定义为一个类的成员函数而不能定义为类的友元函数。
(4) 若一个运算符的操作需要修改对象的状态,选择重载为成员函数较好。
(5) 若运算符所需的操作数(尤其是第一个操作数)希望有隐式类型转换,则只能选用友元函数。
(6) 当运算符函数是一个成员函数时,最左边的操作数(或者只有最左边的操作数)必须是运算符类的一个类对象(或者是对该类对象的引用)。如果左边的操作数必须是一个不同类的对象,或者是一个内部类型的对象,该运算符函数必须作为一个友元函数来实现。
(7) 当需要重载运算符具有可交换性时,选择重载为友元函数。
5.实例:
1)用成员函数来重载运算符:
#include
using namespace std;
class X{
int i;
public:
X(int ii=0){i=ii;}
X operator +(const X &rx){
i+=rx.i;
return X(i);
}
int GetI(){return i;}
};
int main(){
X a(1),b(3);
cout<<(a+b).GetI()<
return 0;
}
2) 用友元函数来重载运算符
说明:此时若用
#include
using namespace std;
则会出现如下的错误:
fatal error C1001: INTERNAL COMPILER ERROR
所以头文件用:#include
#include
using namespace std;
class Complex{
public:
Complex(double r=0.0,double i=0.0){
real=r;
image=i;
}
friend Complex operator+(const Complex&,const Complex&);
void display();
private:
double real;
double image;
};
Complex operator+(const Complex &c1,const Complex &c2){
return Complex(c1.real+c2.real,c1.image+c2.image);
}
void Complex::display(){
cout<<"("<
}
int main(){
Complex c1(3,4),c2(5,-10),c3;
c3=c1+c2;
cout<<"c1=";c1.display();
cout<<"c2=";c2.display();
cout<<"c1+c2=";c3.display();
return 0;
}