C++ 操作符重载
操作符重载,也叫运算符重载,是C++的重要组成部分,它可以让程序更加的简单易懂,简单的运算符使用可以使复杂函数的理解更直观。
虽然运算符重载听起来好像是C++的外部能力,但是多数程序员都不知不觉地使用过重载的运算符。例如,加法运算符“+”对整数、单精度数和双精度数的操作是大不相同的。这是因为C++语言本身已经重载了该运算符,所以它能够用于int、float、double和其它内部定义类型的变量。
操作符重载可对已有的运算符(C++中预定义的运算符)赋予多重的含义,使同一运算符作用于不同类型的数据时导致不同类型的行为。
其目的是:扩展C++中提供的运算符的适用范围,以用于类所表示的抽象数据类型。同一个运算符,对不同类型的操作数,所发生的行为不同。
操作符被重载的基本前提:
1.只能为自定义类型重载操作符;
2.不能对操作符的语法(优先级、结合性、操作数个数、语法结构) 、语义进行颠覆;
3.不能引入新的自定义操作符。
不允许在用户自定义类型中重载的操作符:
允许在用户自定义类型中重载的操作符:
1.操作符重载的定义
在用户自定义类型中使用操作符来表示所提供的某些操作,可以收到同样的效果,但前提是它们与基本类型用操作符表示的操作、与其他用户自定义类型用操作符表示的操作之间不存在冲突与二义性。(即在某一特定位置上,某一操作符应具有确定的、唯一的含义)
编译程序能够对是否存在冲突与二义性作出判断的依据,是类型及其操作集。
例:
#include <iostream> using namespace std; class Complex { public: Complex( ) { real=0; imag=0; } Complex(double r,double i) { real=r; imag=i; } Complex complex_add(Complex &c2); //声明复数相加函数 void display( ); private: double real; //实部 double imag; //虚部 }; Complex Complex::complex_add(Complex &c2) { Complex c; c.real=real+c2.real; c.imag=imag+c2.imag; return c; } void Complex::display( ) { cout<<"("<<real<<","<<imag<<"i)"<<endl; } int main( ) { Complex c1(1,2),c2(3,-4),c3; //定义3个复数对象 c3=c1.complex_add(c2); //调用复数相加函数 cout<<"c1="; c1.display( ); //输出c1的值 cout<<"c2="; c2.display( ); //输出c2的值 cout<<"c1+c2="; c3.display( ); //输出c3的值 return 0; }
程序执行结果:
c1=(1,2i)
c2=(3,-4i)
c1+c2=(4,-2i)
运算符重载的方法是定义一个重载运算符的函数,在需要执行被重载的运算符时,系统就自动调用该函数,以实现相应的运算。从某种程度上看,运算符重载也是函数的重载。但运算符重载的关键并不在于实现函数功能,而是由于每种运算符都有其约定俗成的含义,重载它们应是在保留原有含义的基础上对功能的扩展,而非改变。
重载运算符的函数一般格式如下:
函数类型 operator 运算符名称 (形参表列) { 对运算符的重载处理 }
例如,想将“+”用于Complex类(复数)的加法运算,函数的原型可以是这样的:
Complex operator+ (Complex& c1,Complex& c2);
operator是c++的关键字,专门用于定义重载运算符的函数。operator +就是函数名,表示对运算符+重载。
例:
class Complex { public: Complex( double = 0.0, double = 0.0 ); Complex operator+( const Complex & ) const; Complex operator-( const Complex & ) const; private: double real; // real part double imaginary; // imaginary part }; Complex Complex::operator+( const Complex &operand2 ) const { return Complex( real + operand2.real, imaginary + operand2.imaginary ); } Complex Complex::operator-( const Complex &operand2 ) const { return Complex( real - operand2.real, imaginary - operand2.imaginary ); }
在上例中,+和-运算符重载实际上是有着2个参数,但由于重载函数是类中的成员函数,有一个参数是隐含的,函数是用this指针隐式地访问类对象的成员。
例如
c3=c1+c2
最后在C++编译系统中被解释为:
c3=c1.operator+(c2)
在此例中,operator+是类的成员函数。第一操作数为“*this(c1)”,第二操作数为“参数(c2)”。
实质操作符的重载就是函数的重载,在程序编译时把指定的运算表达式转换成对运算符的调用,把运算的操作数转换成运算符函数的参数,根据实参的类型决定调用哪个操作符函数。
对于单目运算符++和--有两种使用方式,前置运算和后置运算,它们是不同的。针对这一特性,C++约定:如果在自增(自减)运算符重载函数中,无参数表示前置运算符函数,若加一个int型形参,就表示后置运算符函数。
例:
有一个Time类,包含数据成员minute(分)和sec(秒),模拟秒表,每次走一秒,满60秒进一分钟,此时秒又从0开始算。要求输出分和秒的值。
class Time { public: Time( ){minute=0;sec=0;} Time(int m,int s):minute(m),sec(s){ } Time operator++( ); //声明前置自增运算符“++”重载函数 Time operator++(int); //声明后置自增运算符“++”重载函数 private: int minute; int sec; }; Time Time::operator++( ) //定义前置自增运算符“++”重载函数 { if(++sec>=60) { sec-=60; //满60秒进1分钟 ++minute; } return *this; //返回当前对象值 } Time Time::operator++(int) //定义后置自增运算符“++”重载函数 { Time temp(*this); sec++; if(sec>=60) { sec-=60; ++minute; } return temp; //返回的是自加前的对象 }
在代码 Time operator++(int)中,注意有个int,在这里int并不是真正的参数,也不代表整数,只是一个用来表示后缀的标志!!
2.运算符重载函数的两种形式
运算符重载的函数一般地采用如下两种形式:成员函数形式和友元函数形式。这两种形式都可访问类中的私有成员。
友元函数是一种对面对对象程序中类的破坏,可以访问私有成员。
重载为友元函数的运算符重载函数的定义格式如下:
friend函数类型 operator 运算符名称 (形参表列) { 对运算符的重载处理 }
下面用友元函数的形式重载上例:
#include <iostream> using namespace std; class Complex { public: Complex( ) { real=0; imag=0; } Complex(double r,double i) { real=r; imag=i; } friend Complex operator + (Complex &c1,Complex &c2); void display( ); private: double real; double imag; }; Complex Complex::operator+( const Complex &operand2 ) const { return Complex( real + operand2.real, imag + operand2.imag ); } Complex operator + (Complex &c1,Complex &c2) { return Complex(c1.real+c2.real, c1.imag+c2.imag); }
例如
c3=c1+c2
最后在C++编译系统中被解释为:
c3=c1.operator+(c2)
运算符左侧的操作数与函数第一个参数对应,右侧的和第二个参数对应。
当重载友元函数时,将没有隐含的参数this指针。这样,对于双面运算符,友元函数有2个参数,对于单目运算符,友元函数有一个参数。
注意:当同时存在2种形式的定义时,二者也是重载
例:
#include <iostream> using namespace std; class Complex { public: Complex( ) { real=0; imag=0; } Complex(double r,double i) { real=r; imag=i; } Complex operator+( const Complex & ) const; //重载为成员函数 friend Complex operator+(Complex &c1,Complex &c2); //重载为友员函数 void display( ); private: double real; double imag; }; int main( ) { Complex x, y, z; z = x + y; // x.operator+(y) x + 1.0; // operator+( x, 1.0 ) 1.0 + x; // error }
任何用户自定义的隐式转换都不会应用到操作符 . 的左操作数。
对>> and <<的重载
在C++中,系统重载了操作符>>-输入操作 。
成员方法形式
istream& istream::operator>>(short& _Val); istream& istream::operator>>(unsigned short& _Val); istream& istream::operator>>(int& _Val); istream& istream::operator>>(unsigned int& _Val); istream& istream::operator>>(long& _Val); istream& istream::operator>>(unsigned long& _Val); istream& istream::operator>>(void *& _Val);
为自定义类重载操作>>并表示输入操作时,也应采用非成员方式:
istream& operator>>( istream& in, Complex& c ) { in >> c.real >> c.imag; return in; }
Complex c1, c2; cin >> c1; cin >> c2;
class Complex { // 函数访问Complex的非public成员时,必为友元 friend istream& operator>>(istream&, Complex&); // ... };
重载输出操作符<<的要求和形式与重载输入操作符>>相似。
实例:
#include <iostream> #include <string> using namespace std; class Student { friend ostream &operator<<(ostream &,const Student &); friend istream &operator>>(istream &,Student &); public: int num; int age; string sex; string name; }; ostream &operator<<(ostream &output,const Student &s) { cout<<"学号\t"<<"姓名\t"<<"性别\t"<<"年龄\t"<<endl; output<<s.num<<"\t"<<s.name<<"\t"<<s.sex<<"\t"<<s.age<<endl; return output; } istream &operator>>(istream &input,Student &s) { cout<<"学号:"; input>>s.num; cout<<"姓名:"; input>>s.name; cout<<"性别:"; input>>s.sex; cout<<"年龄:"; input>>s.age; return input; } int main() { Student stu; cout<<"Enter Student info:"<<endl; cin>>stu; cout<<"Print Student info:"<<endl; cout<<stu; return 0; }
程序执行结果:
Enter Student info: 学号:2017211758 姓名:wang 性别:man 年龄:21 Print Student info: 学号 姓名 性别 年龄 2017211758 wang man 21