友元函数介绍
在程序设计中,加入类A想访问类B中的私有成员和私有函数时,为了保持程序的封装性,又让其有共享性,就用到了友元类和友元函数。
友元类
class A { friend class B ; public : void Display() { cout << x << endl ; } ; private : int x ; } ; //上面在类A声明了它的友元类是B,所以类B可以访问A中私有成员和私有函数 class B { public : void Set ( int i ) { Aobject . x = i ; } void Display () { Aobject . Display () ; } //调用了类A的私有成员 private : A Aobject ;//声明了类A的对象 } ; void main() { B Bobject ; Bobject . Set ( 100 ) ; Bobject . Display () ; system("pause"); }
友元函数
友元函数应用比较多,常用的场合有:
1. 运算符重载的某些场合需要使用友元。
2. 两个类需要共享数据的时候。
class Test2 { public: //友元函数的特点是:有一个参数是友元类的指针或引用. //友元函数是类外函数,所以声明放在公有段或者私有段没有区别。 friend int OpMem(Test2 *p, int a); //友元函数,中有类对象指针,通过它访问类中私有成员 Test2(int a, int b) { this->a = a; this->b = b; } int getA() { return this->a; } protected: private: int a ; int b; }; int OpMem(Test2 *p, int a) { p->a = a;//获取类中私有成员 return 0; } void main() { Test2 t1(1, 2); t1.getA(); OpMem(&t1, 10);//直接调用友元函数,毕竟友元函数不是成员函数 system("pause"); }
运算符重载
运算符重载是指:为了实现类的多态性(一个函数名有多重含义),运算符与类结合,产生新的含义。
怎么实现运算符的重载?
方式:类的成员函数 或 友元函数(类外的普通函数)
规则:不能重载的运算符有 . 和 .* 和 ?: 和 :: 和 sizeof
友元函数和成员函数的使用场合:一般情况下,建议一元运算符使用成员函数,二元运算符使用友元函数。
1、运算符的操作需要修改类对象的状态,则使用成员函数。如需要做左值操作数的运算符(如=,+=,++)
2、运算时,有数和对象的混合运算时,必须使用友元
3、二元运算符中,第一个操作数为非对象时,必须使用友元函数。如输入输出运算符<<和>>
具体规则如下:
运算符 | 建议使用 |
---|---|
所有一元操作符 | 成员函数 |
= 、()、[]、-> | 必须是成员函数 |
+=、-=、/=等带=的运算符 | 成员函数 |
+、-、*等二目运算符 | 友元函数 |
<<>> | 必须是友元函数 |
函数和返回值
- 如果返回值出现在=左边,作为左值(也可以作为右值),必须是非const引用。
- 如果返回值出现在=右边,只能作为右值,返回const引用或者const值(只读)。
运算符实例
+和-运算符重载
#include <iostream> using namespace std; class PointTest { private: double pointX; public: PointTest(int x1) { pointX=x1; } void PrintResult(); const PointTest operator +(const PointTest &obj);//使用成员函数重载运算符 friend const PointTest operator -(const PointTest &obja,const PointTest & objb);//友元函数 }; #pragma region 成员函数 const PointTest PointTest::operator+(const PointTest &obj) { return PointTest(this->pointX + obj.pointX); } void PointTest::PrintResult() { cout<<this->pointX<<endl; //system("pause"); getchar(); } #pragma endregion //友元函数重载+ const PointTest operator-(const PointTest &obja,const PointTest & objb) { return PointTest(obja.pointX-objb.pointX); } int main() { PointTest a(1); PointTest b(2); PointTest c = a+b;//调用成员函数 c.PrintResult(); c =c-b; c.PrintResult(); a-1;//正确,先调用类型转换函数,把1变成对象,之后调用友元函数 a+1;//同上,但是参数必须是const类型,才编译通过 1-a; //1+a;调用成员函数时,第一个操作数必须是对象,因为第一个操作数还有调用成员函数的功能 }
1、由于+ -都是出现在=号的右边,如c=a+b,即会返回一个右值,可以返回const型值
2、后几个表达式讨论的就是,数和对象混合运算符的情况,一般出现这种情况,常使用友元函数
单目运算符++ 、–
class Point { private: int x; public: Point(int x1) { x=x1;} Point operator++();//成员函数定义自增 const Point operator++(int x); //后缀可以返回一个const类型的值 friend Point operator--(Point& p);//友元函数定义-- friend const Point operator--(Point& p,int x);//后缀可以返回一个const类型的值 }; Point Point::operator++()//++obj { x++; return *this; } const Point Point::operator++(int x)//obj++ { Point temp = *this; this->x++; return temp; } Point operator--(Point& p)//--obj { p.x--; return p; //前缀形式(--obj)重载的时候没有虚参,通过引用返回*this 或 自身引用,也就是返回变化之后的数值 } const Point operator--(Point& p,int x)//obj-- { Point temp = p; p.x--; return temp; // 后缀形式obj--重载的时候有一个int类型的虚参, 返回原状态的拷贝 } int main() { Point b(2); a++;//隐式调用成员函数operator++(0),后缀表达式 ++a;//隐式调用成员函数operator++(),前缀表达式 b--;//隐式调用友元函数operator--(0),后缀表达式 --b;//隐式调用友元函数operator--(),前缀表达式 cout<<a.operator ++(2);//显式调用成员函数operator ++(2),后缀表达式 cout<<a.operator ++();//显式调用成员函数operator ++(),前缀表达式 cout<<operator --(b,2);//显式调用友元函数operator --(2),后缀表达式 cout<<operator --(b);//显式调用友元函数operator --前缀表达式 }
在前置++(++a)和后置++(a++)中,前后缀仅从函数名(operator++)无法区分,只能有参数区分,这里引入一个虚参数int x,x可以是任意整数。
重载运算符[]
在这里将讲到返回对象引用。
class Point { private: int x[5]; public: Point() { for (int i=0;i<5;i++) { x[i]=i; } } int& operator[](int y); //声明的返回对象的引用 }; int& Point::operator[](int y) { static int t=0; if (y<5) { return x[y]; } else { cout<<"下标出界"; return t; } } int main() { Point p; int a = p[3];//此处为右值,可以为变量,也可以为引用。 a[2] = 3;//此处为对象成为左值,必须是对象引用,如果返回值为对象变量的时候,编译器不知道如何转化。 }
运算符[]中的重载方式:只能使用成员函数重载。
函数名:operator
参数表:一个参数,且仅有一个参数,该参数设定了下标值,通常为整型,但是也可以为字符串( 看成下标)。
返回值: 返回的值可以做左值,也可以做右值,则必须使用返回引用。
难点重载运算符<<,>>
这里写代码片class Point { private: int x; public: Point(int x1) { x=x1;} friend ostream& operator<<(ostream& cout,const Point& p);//使用友元函数重载<<输出运算符 friend istream& operator>>(istream& cin,Point& p);//使用友元函数重载>>输出运算符 }; //对于<<这种运算符,格式相对比较固定,返回类型还有参数。 ostream& operator<<(ostream& cout,const Point& p) { cout<<p.x<<endl; return cout; } istream& operator>>(istream& cin,Point& p) { cin>>p.x; return cin; } Point a(1); Point b(2); cin>>a>>b; cout<<a<<b<<endl;
语法:
重载方式:只能使用友元函数重载 且 使用三个引用&
函数名:
输出流: operator<<(参数表)
输入流:operator>>(参数表)
- 输出流
输出流: 必须是两个参数:对输出流ostream& 和 对象第一个操作数cout,定义在文件iostream中,是标准类类型ostream的对象的引用。
如:ostream& cout,const Point& p
ostream& operator<<(ostream& cout,const 对象引用) - 输入流
必须是两个参数:对输入流ostream& 和 对象
第一个操作数是cin,定义在文件iostream,实际上是标准类类型istream的对象的引用
如:instream& cin,const Point& p
istream& operator>>(istream& cin,对象引用)
成员函数要求是有对象调用,则第一个参数必须是类的对象,但是<<和>>第一个参数是流的对象引用。不能使用成员函数
版权声明:本文为博主原创文章,未经博主允许不得转载。