操作符重载的实现

如题所述

第1个回答  2016-05-18

一般来说操作符重载函数一般不要求作为类的成员函数或者是友元函数,一般情况下可以将操作符重载函数作为类的成员函数。但是有一种情况必须要求操作符函数作为类的友元函数或者是独立的函数,就是一个内置类型和对象相加的情况。比如有语句m+1和1+m第一条可以在类中定义操作符函数的形式为hyong operator +(int i){},语句m+1可以调用这个函数是正确的,但对于1+m就不能调用这个函数了,因为类中的操作符重载函数是最左边的对象是调用该函数的对象,但1+m最左边的是一个内置整型类型1,所以不会调用这条语句,对于这种语句就只能把操作符重载函数定义为独立的函数或类的友元函数即形如hyong operator +(int i,hyong a){}这样1+m就会转换成operator +(1,m)这样就是正确的。当然如果这个操作符重载函数需要访问类中的私有成员时,就应把该函数定义为类的友元函数,如果不需要访问类中的私有成员,则可以定义为友元也可以定义为独立函数。
必须把它作为类成员函数的运算符有:(),[],->;和任何赋值运算符,重载这些运算符时必须把操作符函数声明为类的成员函数。 1 并不是所有的操作符都能被重载。除了. ,.* ,:: ,? : ,sizeof,typeid这几个运算符不能被重载,其他运算符都能被重载 ⒏2 重载不能改变该运算符用于内置类型时的函义,程序员不能改变运算符+用于两个int型时的含义。
⒏3运算符函数的参数至少有一个必须是类的对象或者类的对象的引用。这种规定可以防止程序员运用运算符改变内置类型的函义。
⒏4 重载不能改变运算符的优先级。
⒏5 重载不能改变运算符的结合律。
⒏6 重载不能改变运算符操作数的个数。比如+需要两个操作数,则重载的+也必须要有两个操作数。 1. 注意++有前缀和后缀之别,前缀形式是变量先加1然后执行表达式,而后缀形式则是先执行表达式然后再使变量加1,所以在执行后缀的++运算符时应先返回对象的原始值,然后才对对象加1。
//20131030 上述说法不准确。前缀++返回的是左值,即该对象+1之后的引用。后缀++返回是的右值。类似于{ int tmp = a; a=a+1; return tmp;} 并不是先执行表达式再加1 而是返回+1之前的值。可以通过下面的程序印证: int a =5; (++a) = 10;可以通过编译 (a++) = 10;无法通过编译
2. 默认的带有一个参数的++运算符函数是前缀++运算符,要重载后缀的++运算符必须采用另一种方式实现。
重载后缀的++运算符时应给函数多增加一个int参数,该int参数不会使用,应忽略他,该参数只是为了让编译器区分++运算符的前缀和后缀形式之间的区别。比如重载后缀++运算符的友元函数形式为hyong operator ++(hyong &a,int i){}后面的参数int i没有实际意义,应忽略他。
例:重载++运算符的例子
class A
{public: int b; A(){b=0;} A(int i){b=i;} ~A(){cout<<xi<<\n;}
const A & operator ++(){ ++b; return *this;} }; //定义前缀形式的++运算符,函数的返回类型既可以返回引用也可以是返回值,但返回引用不会增加内存开销。返回类型可以是任意的,比如可以是int型,也可以是void,即没有返回值,但这里的返回类型必须是类类型A,因为在main函数中表达式用于赋值运算符的左边,且把结果赋给了一个类A的对象。返回const的原因是防止++++k这样的情况出现,有const就不能再改变返回对象的值即不能再对++k作增量运算。
const A & operator ++(A &j,int i) //定义独立的后缀形式的++运算符,这里必须把第一个参数声明为对对象的引用,因为++运算符会改变原始对象的值,如果不是引用就不能改变原始对象的值,也就达不到++运算符的效果。注意int i参数是不使用的,只是让编译器区分是前缀还是后缀的++运算符。
{A t(j); //定义一个A类对象t,因为后缀形式的++运算符是先执行表达式后进行增量运算,所以这里应生成一个临时对象以便返回对象的原始值
++j.b; //注意,因为独立函数既不是类的友元又不是类的成员,所以这里没有this指针,也不能直接访问类的成员。
return t;} //返回对象t,这里会生成一个临时对象。
int main()
{ A m,n,k;
m=++k; //调用前缀形式的++类成员运算符函数,语句等价于m=k.operator ++();因为前缀的++是类的成员,所以只能用点运算符调用,形如operator ++(k)将是错误的。
cout<<m.b<<k.b; //输出11,前缀++是先使变量加再执行表达式,即对象k的值先加,然后再赋给对象m
n=k.operator ++ (); //显示调用前缀的++类成员运算符函数。同m=++k。
cout<<n.b<<k.b; //输出22。
n=k++; //调用后缀形式的独立++运算符函数,该语句等价于n=operator ++(k,1),其中后面的是没有意义的参数,只是为了让编译器区别是前缀还是后缀
cout<<n.b<<k.b; //输出23,注意,这里n的值没有增加,因为后缀++是先执行表达式后使变量加。
n= operator ++(k,1); //显示调用后缀的++独立运算符函数,同n=k++。注意整形实参在这里没有实用价值,但必须得有,以便指明是调用的后缀++形式。
cout<<n.b<<k.b; } //输出34。 要注意重载二元运算符时如果有形如1+m这种类型的表达式就必须把该操作符函数定义为非类的成员的形式。因为类中的操作符重载函数是最左边的对象是调用该函数的对象
class A
{public: int b; A(){b=0;} ~A(){cout<<xi<<\n;}
explicit A(int i){b=i;} //这里需要explicit关键字以防止自动的隐式类型转换,不然语句const A &operator +(const A &j)和friend const A &operator +(const A &j,const int i)将出错二义性问题。也就是第一个操作符函数可以用隐式类型转换用语句m+3来调用。
//const A operator +(const A &j){b=b+j.b;return *this;} //对于+操作符不会改变被加的操作数的值,但这里改变了调用该函数的对象的值,最好不要这样做。
const A operator +(const A &j){A t; t.b=b+j.b; return t;} //定义一个临时对象t以防止修改调用该函数的两个操作数的值。
friend const A &operator +(const A &j,const int i); };//+操作符函数不应该改变调用该函数的两个操作数的值,所以这里将参数声明为const
const A operator +(const A &j,const int i){A t;t.b=j.b+i; return t;} //定义友元函数以实现对象+内置类型这种类型的运算
const A operator +(const int i,const A &j) {A t; t.b=j.b+i;return t;} //因为类A没有私有成员,该函数也不访问类中的私有成员所以可以不用定义为类的友元,这个函数将实现,内置类型+对象的这种运算
//对于这种操作符重载函数不能定义为类的成员函数,因为类的成员函数的第一个参数是指向类对象的一个this指针,在这里第一个参数是内置类型不是类的对象,所以必须把这种操作符函数定义为非成员的形式。
int main()
{ A m⑴,n⑵,k;
k=m+3; //调用operator +(const A &j,const int i)这个类的友元操作符函数,以实现对象和内置类型相加,该语句和operator +(m,3)一样。
cout<<m.b<<k.b; //输出14,这里没有改变对象m的值,这是应该的。
k=operator +(m,3); cout<<m.b<<k.b;// 输出14。和上面m+3的语句一样,这是显示调用操作符函数的形式,记住操作符重载函数是一个函数,只是他的名字比较特别,名字为operator加上后面的操作符而已
k=m+n; //调用类成员操作符函数operator +(const A &j),实现两个对象相加,这条语句会自动转换为m.operator +(n)的形式调用操作符函数。记住最左边的对象是调用类成员操作符函数的对象。cout<<m.b<<n.b<<k.b; //输出123,这里没有改变对象m和n的值,实现了对象m和n相加 k=n.operator +(m); cout<<m.b<<n.b<<k.b; //显示调用成员操作符函数operator +(const A &j)的形式,这种形式和语句n+m等价。
k=4+m; //调用操作符函数operator +(const int i,const A &j)以实现内置类型和一个对象相加,语句和operator +(3,m)等价。cout<<m.b<<k.b; //输出15。k=operator +(4,m); cout<<m.b<<k.b; } //输出15,语句和+m相同。 1. 注意重载赋值运算符和[],(),->;运算符必须定义为类的成员函数。
2. 注意:如果程序不提供显示的赋值运算符则系统会提供一个默认的赋值运算符。
3. 什么时候重载赋值运算符:当类中含有指针成员时,一般都要重定义类的赋值运算符。
4. 重载赋值运算符时应有处理语句m=m的情况。其中m是某一个类的对象。如果不处理这样的语句有时会出现问题,具体什么问题有待调查。可以用this指针来做处理,比如有语句const A & operator(A &j)则可以用if(this==&j) return *this;这样的语句来处理,即比较当前调用赋值运算符函数的对象的地址和被赋值的对象的地址,如果地直相等说明是同一个对象。
5. 重载赋值运算符时应返回一个对象。因为赋值运算符的左边是一个对象,所以重载赋值运算符应返回一个类的对象,为了避免不必要的开销,最好是返回一个类的对象的引用。
6. 重载赋值运算符时必须是类的成员函数。
class A
{
public:
int b;
A(){b=1;}
A(int i){ b=i;}
const A & operator =(const A & j) //返回一个类的对象的引用
{
/* 用this和j的地址来检查是否是对自身的赋值的情况,如果调用赋值运算符函数的地址和
被赋值的对象的地址相等,则说明是同一个对象,就返回当前对象。
*/
if(this==&j) return *this;
b=j.b;
return *this;
}
};
int main()
{
A m⑵; A n; n=m; cout<<n.b;
n=n; //对象对自已赋值的情况。
cout<<n.b;
}

相似回答