第三章、友员、重载和引用
3.1友员
友员的作用:为不同类(或对象)的成员函数,类成员函数和一般函数之间进行数据共享。
友员有三种:友员函数、友员成员和友员类
注意:友员可以直接访问私有成员因此可以提高效率,但也会降低数据的封装性
3.1.1友员的定义:在要说明的友员函数或友员类前面加上关键字friend。
如果一个类的私有成员允许外界访问,则可将友员声明放在该类中,放的位置可以是私有部分,也可以是公有部分。
例如:
class Point
{ .....
public:
friend void show(Point p);//声明 show()是友员函数
friend class line; //声明line是Point类的友员类
};
3.1.2友员函数
上面的例子中发现,友员函数(友员类)都需要向它传递一个对象参数,这是因为:show()友员函数并不是Point类的成员函数,没有this指针,在调用时无法确定私有数据所属的对象。
3.1.3友员成员
友员函数除了是外部独立函数外,还有可能是另外一个类的成员函数。它的说明是在声明友员函数时加上成员函数所在的类名。这样做的好处是两个类可以相互合作,协调工作,完成某一项任务。
例如:有两个类一个是学生类,一个是教师类,通过教师类对学生类中的成绩进行修改
#include <iostream.h> //或者#include <iostream>
//using namespace std
class Teacher
{
public:
void Rework(Student &p, float y);
};
class Student
{
float grade;
public:
Student(float x){grade = x};
void print()
{ cout<<"grade = "<<grade<<endl;}
friend void Teacher::Rework(Student &p, float y);//将教师类的成员函数声明为学生类的友员
}
....
3.1.4友员类
友员可以是函数,也可以是一个类。当一个类是另一个类的友员时,意味着该类中的成员函数都可以访问另一个类中的私有数据成员。
例如:
#include<iostream.h>
class Array
{
int a[10];
public:
int Set();
friend class Lookup;//查找类是数组类的友员
};
class Lookup
{
public:
void Max(Array x);//查找最大值成员函数
void Min(Array x);//查找最小值成员函数
}
...
3.2重载
意义:通过函数重载和运算符重载,可以使具有相同函数名的函数或运算符在不同场合有着不同的操作。
3.2.1函数重载
具体说:几个函数取同一个名字,通过使用的参数个数或者参数类型不同来调用不同版本的函数。(这个前面讲构造函数的时候就已经提到了)
一、构造函数重载(前面讲过了)
二、成员函数重载
#include<iostream.h>
class Show
{//...
public:
void print(char cc);
void print(char *);
void print(int xx,char pp);
};
void Show::print(char cc)
{
int i;
for(i=0;i<25;i++)
cout<<cc;
cout<<endl;
}
void Show::print(char *str)
{
cout<<str<<endl;
}
void Show::print(int xx,char pp)
{
for(int i=0;i<xx;i++)
cout<<pp;
cout<<endl;
}
void main()
{
Show c1,c2,c3;
char *str="This is c++ program";
c1.print('*');
c2.print(str);
c3.print(25,'*');
}
三、非成员函数重载
类以外的独立函数也允许重载
#include<iostream.h>
#include<string.h>
int Add(int x,int y)
{
return x+y;
}
double Add(double x,double y)
{
return x+y;
}
char *Add(char *x,char *y)
{
return strcat(x,y);
}
/*原型
extern char *strcat(char *dest,char *src);
用法
#include <string.h> 在C++中,则存在于<cstring>头文件中。
功能
把src所指字符串添加到dest结尾处(覆盖dest结尾处的'\0')并添加'\0'。
说明
src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。 返回指向dest的指针
*/
void main()
{
int x1=3,y1=5;
double x2=12.8,y2=35.7;
char str[20]="这是一个";
char *str1=&str[0], *str="C++程序":
cout<<"x1+y1="<<Add(x1,y1)<<endl;
cout<<"x2+y2="<<Add(x2,y2)<<endl;
cout<<"s1+s2="<<Add(str1,str2)<<endl;
}//这里对char *str和char str[]的区别,可以看这两篇文章点击打开链接和点击打开链接,讲得很清楚。
3.2.2运算符重载
一、运算符重载的形式
运算符重载实际上就是函数重载。运算符重载有两种:一种是重载为类的成员函数,另一种是重载为类的友员函数。
1.重载为类的成员函数
将运算符重载为类的成员函数的一般形式如下:
type operator 运算符(参数表)
type指的是函数返回的类型;operator是定义的运算符重载函数的关键字;“运算符”是要重载的运算符符号。参数表中的参数是根据所需要重载的运算符来决定。如果所需要重载的运算符是单目时,参数表是空的,此时当前对象作为运算符的单操作数。如果运算符是双目时,参数表中有一个操作数,运算时左操作数是当前对象本身的数据,参数表中的操作数为右操作数。
例如:
class Add
{
int x;
public:
...
Add operator + (Add p1);
...
}
Add Add::operator +(Add p1)
{
Add p;
p.x = x+p1.x;
return p;
}
void main()
{
Add p1,p2;
p1=p1+p2;//两个对象相加,等价于执行p1.operator+(p2)
}
2.重载为类的友员函数
重载形式与重载为类的成员函数相同,只是参数表中的参数要求不同。如果重载的运算符是单目时,则参数表必须有这一个操作数;如果运算符是双目时,则在参数表中必须有两个操作数。也就是说重载为类的友员函数时,所有的操作数均需用参数来传递。
例如:
class A
{
int x;
public:
...
friend A operator +(A p1,A p2);
...
}
A operator +(A p1,A p2)
{
A p;
p.x = p1.x +p2.x;
return p;
}
void main()
{
...
p1=p1+p2;//等价于执行operator+(p1,p2)
...
}
二、其他运算符的重载
1.自加(++)和自减(--)运算符(采用成员函数形式进行重载)
2.赋值运算符重载(只能重载为成员函数)
3.运算符[ ]和( )的重载(只能重载为成员函数)
4.关系运算符重载(一般重载为类的友员函数)
例如
class Circle
{
double r,acre;
public:
Circle(double rr){r=rr;}
double Acre()
{
acre=3.14*r*r;
return acre;
}
friend int operator <(Circle c1,Circle c2)//对于友员函数重载,双目运算参数要有两个
{
return c1.acre<c2.acre?1:0;
}
};
5.输入输出流运算符重载
意义:使得用户自定义的数据类型的输入输出也可以像系统标准类型的输入输出那样直接、方便。它们一般采用友员函数的方式来重载。
定义格式:
istream &operator>>(istream &in, user_type &obj)
{
in>>obj.item1;
in>>obj.item2;
//...
return in;
}
ostream &operator<<(ostream &out, user_type &obj)
{
out<<obj.item1;
out<<obj.item2;
//...
return out;
}
例如
#include<iostream.h>
class point
{
int x,y;
public:
point(int x1=0,int y1=0){x=x1;y=y1;}
friend istream &operator>>(istream &input,point &obj);
friend ostream &operator<<(ostream &input,point &obj);
};
istream &operator>>(istream &input,point &obj)
{
cout<<"Input the x,y of point:\n";
input>>obj.x;
input>>obj.y;
return input;
}
ostream &operator<<(ostream &output,point &obj)
{
cout<<"output the x,y of point:\n";
output<<"x="<<obj.x<<",";
output<<"y="<<obj.y<<"\n";
return output;
}
void main()
{
point obj1;
cin>>obj1;
cout<<obj1;
}
//输入输出使用的是重载为类的友员函数
3.3引用
3.3.1引用的概念:引用就是给对象起个别名,作用是给函数传递大型的对象或从函数中返回左值。
定义的引用的格式是: type & 名称
type:要引用的类型
名称:引用的名字
例如:引用一个整型变量
int sum;
int &count=sum;//创建一个整型引用count,count是目标sum的地址的别名
引用传递参数;
引用返回值:引用表达式可以做左值表达式使用
//引用返回值(返回值是引用类型)
#include<iostream.h>
int score[4][5]={{60,70,80,90,78},{75,85,88,78,83},{89,88,79,96,90},{76,74,69,90,87}};
int &level(int grade[],int unit,int &gA,int &gB);
void main()
{
int genusA=0,genusB=0,i;
int student=4;
int gradeunit=5;
for(i=0;i<student;i++)
level(score[i],gradeunit,genusA,genusB)++;//给返回引用赋值
cout<<"A 级数人为"<<genusA<<endl;
cout<<"B 级数人为"<<genusB<<endl;
}
int &level(int grade[],int unit,int &gA,int &gB)
{
int sum=0;
for(int i=0;i<unit;i++)
sum=sum+grade[i];
sum=sum/unit;
if(sum>=85)
return gA;
else
return gB;
}
//函数level()返回一个引用,由于返回的是引用,所以可以作为左值直接进行自加操作
例如:
#include<iostream.h>
#include<string.h>
struct Vecelem
{
char *name;
int value;
};//定义数组中元素的类型
class Assocvec
{
Vecelem *elems;
int dim;
int used;
public:
Assocvec(int dim);
~Assocvec();
int & operator [](char *idx);
void Print();
};
Assocvec::Assocvec(int dim)
{
Assocvec::dim=dim;
used=0;
elems=new Vecelem[dim];
}
Assocvec::~Assocvec()
{
for(int i=0;i<used;i++)
delete elems[i].name;
delete elems;
}
int & Assocvec::operator[](char *idx)//定义重载运算符[],返回一个整型变量的引用
{
for(int i=0;i<used;i++)//在数组中查找idx向量
if(strcmp(idx,elems[i].name)==0)//找到则返回向量的值
return elems[i].value;
if(used<dim&&(elems[used].name=new char[strlen(idx)+1])!=0)//分配的字符串空间也包含了"\0"
{//没找到
strcpy(elems[used].name,idx);
elems[used].value=used+1;
return elems[used++].value;//返回值是elems[used].value,随后副作用使used自加
}
static int dummy=0;//定义一个静态变量的目的是因为要返回引用,该语句执行的条件是当数组发生越界或者申请
//内存空间失败时才执行
return dummy;
}
void Assocvec::Print()
{
for(int i=0;i<used;i++)
cout<<elems[i].name<<" "<<elems[i].value<<"\n";
}
void main()
{
Assocvec count(16);
count["apple"]=15;
count["orange"]=10;
count["fruit"]=count["apple"]+count["orange"];
count.Print();
}
对“[ ]”重载是一个返回引用整数的函数,函数有三个作用:
一是当给定参数的字符串不是当前向量组中的成员时,将其添加到向量组,并返回临时赋的向量值
二是当它的名字与向量组的一个成员的向量名相同时,返回它的向量值
三是可以作为表达式左项,修改该向量的向量值
3.3.5常引用
当使用const关键字说明引用时,则被说明的引用是常引用,常引用所引用的对象不能被更新。
常引用定义格式如下:
const 类型 & 引用名
例如:
const int &m;//当不希望函数的参数在传递过程中被意外更改时,可以用常引用做形参
例:
#include<iostream.h>
float mul(const float &x,const float &y)
{
return x*y;
}
void main()
{
float a=10.5,b=23.5;
cout<<a<<"*"<<b<<"="<<mul(a,b)<<endl;
}