最近在学习C++的时候突然发现有一个很好玩的东西,就是运算符的重载。
运算符重载是对已有的运算符赋予更多的含义,使同一个运算符会有意想不到好处与方便。
下面我们就简单地介绍运算符的重载。
1.首先在C++中,运算符都被当成函数,C++允许程序自己来规定预算符如何工作,
方法就是自己定义相应的运算符函数,比如operator&,operator+,operator<<,称为运算符重载。
2.运算符重载主要有两种形式
A.友元函数(全局函数)形式:
定义格式: 返回类型 operator (参数表)
******双目运算符:返回类型 operator 运算符(操作数1,操作数2)-->等价:(操作数1)运算符(操作数2)
******单目运算符:返回类型 operator 运算符(操作数)-------------->等价:运算符(操作数)
解释:在全局函数中如果需要访问对象的非公开成员,需要在类中对这个全局变量函数进行授权,
方式是在类里面用关键字friend声明这个函数为友元,友元函数不是成员函数,一般在外面定义,也可以在类里面friend声明的地方直接定义,但是他依旧是友元,不是成员。如果需要的话,一个类A也可以吧另外一个类B声明为友元,这等同于把B类的所有成员函数都等同于A类的友元了。一般不这样做,而友元声明是单向的,不传递的。
程序例子:
B.成员函数形式:
定义格式:返回类型 operator (除第一个操作符之外的参数)
******双目运算符: 返回类型 operator 运算符(操作数2)
******单目运算符: 返回类型 operator 运算符()//无操作数
解释:成员函数定义的时候,第一个操作数作为当前对象,不需要做实参传递,只需传递剩余的操作数就可以了。在运算函数中可以通过调用*this或者this->来访问第一个操作数。
程序例子:
3.在运算符重载运用时应该注意以下几个问题:(转来的,觉得总结的不错)
(1)C++中只能对已有的C++运算符进行重载,不允许用户自己定义新的运算符;
(2)C++中绝大部分的运算符可重载,除了成员访问运算符.,成员指针访问运算符.*,作用域运算符::,长度运算符sizeof以及条件运算符?:;
(3)重载后不能改变运算符的操作对象(操作数)的个数。如:"+"是实现两个操作数的运算符,重载后仍然为双目运算符;
(4)重载不能改变运算符原有的优先级;
(5)重载不能改变运算符原有结合的特性。比如:z=x/y*a,执行时是先做左结合的运算x/y,重载后也是如此,不会变成先做右结合y*a;
(6)运算符重载不能全部是C++中预定义的基本数据,这样做的目的是为了防止用户修改用于基本类型数据的运算符性质;
(7)从上述的示例中可以看到双目运算符可以被重载为友元函数也可以重载为成员函数,但有一种情况,只能使用友元函数,
4.具体例子
A。重载运算符: new/delete
5.小结运算符重载
A -> 返回内部某个结构变量的地址,编译器通过这个地址执行正正的->操作范文里面的变量。
B new/delete 重载负责内存分配和释放,编译器会在内存分配后调用构造函数,内存释放之前
调用析构函数。有[]与没有[]是不同的运算符。
C ++/--
前++前--,正常的单目运算用法,因为计算的结果就是对象的最新值,因此可以直接那这个对象
作为计算结果,返回类型是引用。
成员:A& operator++ (){......return *this;}
友元:friend A& operator++ (A& x){.......return x;}
后++后--,非正常用法,计算结果是对象额旧值,必须用临时空间来保存,不能那这个对象本生
作为就算结果,需要多余int型的哑元。
成员:A& operator++ (int){......return old;}
友元:friend A operator++ (A& x,int){.......return old;}
*******运算符重载一定要合乎情理。运算符是自己规定运算符对于特定类型的操作数应该如何工作。
*******对于基本类型的运算不应该重载,不应该创造行的运算符,不应该改变操作数个数。
*******有些运算符不允许重载:‘.’‘.*’‘::’‘?:’
运算符重载是对已有的运算符赋予更多的含义,使同一个运算符会有意想不到好处与方便。
下面我们就简单地介绍运算符的重载。
1.首先在C++中,运算符都被当成函数,C++允许程序自己来规定预算符如何工作,
方法就是自己定义相应的运算符函数,比如operator&,operator+,operator<<,称为运算符重载。
2.运算符重载主要有两种形式
A.友元函数(全局函数)形式:
定义格式: 返回类型 operator (参数表)
******双目运算符:返回类型 operator 运算符(操作数1,操作数2)-->等价:(操作数1)运算符(操作数2)
******单目运算符:返回类型 operator 运算符(操作数)-------------->等价:运算符(操作数)
解释:在全局函数中如果需要访问对象的非公开成员,需要在类中对这个全局变量函数进行授权,
方式是在类里面用关键字friend声明这个函数为友元,友元函数不是成员函数,一般在外面定义,也可以在类里面friend声明的地方直接定义,但是他依旧是友元,不是成员。如果需要的话,一个类A也可以吧另外一个类B声明为友元,这等同于把B类的所有成员函数都等同于A类的友元了。一般不这样做,而友元声明是单向的,不传递的。
程序例子:
#include<iostream>
using namespace std;
class R{
int n;//分子
int d;//分母
public:
R(int nn,int nd=1):n(nn),d(nd)
{
if(d<0) n=-n,d=-d;
for(int i=d;i>1;i--)
{
if(d%i==0&&n%i==0){
d/=i,n/=i;
break;
}
}
}
friend ostream& operator<<(ostream& o,const R& x)//里面定义的友元函数
{//这个函数是对运算符<<进行的重载,因为<<的返回类型是ostrean。所以返回的参 //数类型也是ostream。
o<<x.n<<"/"<<x.d;
return o;
}
friend R operator+(const R& x,const R& y);里面声明的友元函数
};
R operator+(const R& x,const R& y) //友元函数在外面定义
{ //这个是对+运算符进行的重载,
return R (x.n*y.d+x.d*y.n,x.d*y.d);
}
int main(){
R a(2,4);R b(1,3); //定义两个对象
cout<<a<<","<<b<<endl; //输出a,b的值,利用了输出运算符重载函数
//等价于operator<<(cout,a)....
cout<<a+b<<endl; //使a+b,并输出。利用了输出运算符重载函数和加 //法运算符重载等价于operator+(a,b)
return 0;
B.成员函数形式:
定义格式:返回类型 operator (除第一个操作符之外的参数)
******双目运算符: 返回类型 operator 运算符(操作数2)
******单目运算符: 返回类型 operator 运算符()//无操作数
解释:成员函数定义的时候,第一个操作数作为当前对象,不需要做实参传递,只需传递剩余的操作数就可以了。在运算函数中可以通过调用*this或者this->来访问第一个操作数。
程序例子:
#include<iostream>
#include<cstring>
using namespace std;
class P
{
string name;
int age;
float salary;
public:
P(const char* n,int a,float s):name(n),age(a),salary(s){}
operator float(){return salary;}//这个就是成员函数的运算符重载
// operator 类型(){}这是重载类型运 //算符
operator int (){return age;}
operator string(){return name;}
};
int main()
{
P a("Tom",18,8000);
string info =a;//自动转化类型
float money =a;
int age =a;
cout<<info<<","<<age<<","<<money<<endl;
return 0;
}
3.在运算符重载运用时应该注意以下几个问题:(转来的,觉得总结的不错)
(1)C++中只能对已有的C++运算符进行重载,不允许用户自己定义新的运算符;
(2)C++中绝大部分的运算符可重载,除了成员访问运算符.,成员指针访问运算符.*,作用域运算符::,长度运算符sizeof以及条件运算符?:;
(3)重载后不能改变运算符的操作对象(操作数)的个数。如:"+"是实现两个操作数的运算符,重载后仍然为双目运算符;
(4)重载不能改变运算符原有的优先级;
(5)重载不能改变运算符原有结合的特性。比如:z=x/y*a,执行时是先做左结合的运算x/y,重载后也是如此,不会变成先做右结合y*a;
(6)运算符重载不能全部是C++中预定义的基本数据,这样做的目的是为了防止用户修改用于基本类型数据的运算符性质;
(7)从上述的示例中可以看到双目运算符可以被重载为友元函数也可以重载为成员函数,但有一种情况,只能使用友元函数,
4.具体例子
A。重载运算符: new/delete
#include<iostream>
#include<cstdlib>
using namespace std;
class A{
int data;
public:
A(int d=0):data(d)
{
cout<<"A("<<data<<")"<<endl;
}
~A()
{
cout<<"~A("<<data<<")"<<endl;
}
void* operator new(size_t (bytes)) //重载 new
{
cout<<"bytes="<<bytes<<endl;
return malloc(bytes);
}
void operator delete(void* p) //重载 delete
{
cout<<"free "<<p<<endl;
free(p);
}
void* operator new[](size_t (bytes)) //重载 new[]
{
cout<<"Bytes="<<bytes<<endl;
void* P = malloc(bytes);
cout<<"malloc:"<<P<<endl;
return P;
}
void operator delete[](void* p) //重载 delete[]
{
cout<<"Free="<<p<<endl;
free(p);
}
};
int main()
{
A* p = new A; //operator new(sizeof(A))
cout<<"sizeof A="<<sizeof(A)<<endl;
cout<<"p="<<p<<endl;
delete p; //operator delete(p)
p=NULL;
cout<<"__________"<<endl;
cout<<p<<endl;
delete[] p;p=NULL;
return 0;
}
5.小结运算符重载
A -> 返回内部某个结构变量的地址,编译器通过这个地址执行正正的->操作范文里面的变量。
B new/delete 重载负责内存分配和释放,编译器会在内存分配后调用构造函数,内存释放之前
调用析构函数。有[]与没有[]是不同的运算符。
C ++/--
前++前--,正常的单目运算用法,因为计算的结果就是对象的最新值,因此可以直接那这个对象
作为计算结果,返回类型是引用。
成员:A& operator++ (){......return *this;}
友元:friend A& operator++ (A& x){.......return x;}
后++后--,非正常用法,计算结果是对象额旧值,必须用临时空间来保存,不能那这个对象本生
作为就算结果,需要多余int型的哑元。
成员:A& operator++ (int){......return old;}
友元:friend A operator++ (A& x,int){.......return old;}
*******运算符重载一定要合乎情理。运算符是自己规定运算符对于特定类型的操作数应该如何工作。
*******对于基本类型的运算不应该重载,不应该创造行的运算符,不应该改变操作数个数。
*******有些运算符不允许重载:‘.’‘.*’‘::’‘?:’