C++运算符重载总结
重载,就是赋予新的含义,运算符重载和函数重载类似,同一个运算符可以有不同的功能。
运算符重载的方式就是定义一个函数,在函数体内实现想要的功能,当用到该运算符时,编译器就会自动调用这个函数。也就是说,运算符重载就是通过函数定义实现的,它本质上是函数重载。
返回值类型 operator 运算符名称(形参列表){
//todo;
}
operator是关键字,专门用于定义重载运算符的函数。
重载运算符的规则:
1):并不是所有运算符都可以重载。例如长度运算符(sizeof)、条件运算符(: ?)、成员选择符(.)、对象选择符(.*)、域解析符(::)不能被重载。 2):重载不能改变运算符的优先级和结合性。 3):重载不会改变运算符的用法。例如"+"号总是出现在两个操作数之间。 4):重载运算符的函数不能有默认的参数,否则就改变了运算符操作数的个数,显然是错误的。
下面的代码定义了一个复数类,通过运算符重载,用”+”号实现了复数的加法。
/************************************************************************* > file name: 运算符重载.cpp > author: tanswer_ > mail: 98duxm@gmail.com > created time: 2016年10月15日 星期六 19时49分34秒 ************************************************************************/ #include #include #include #include #include #include #include #include #include #include using namespace std; class complex { private: double real; //实部 double imag; //虚部 public: complex(): real(0.0),imag(0.0) {} complex(double a,double b):real(a),imag(b) {} complex operator +(const complex & a) const; //类内声明+重载 void display() const; }; /*运算符重载*/ complex complex::operator+(const complex & a) const { /*简单的写法*/ return complex(real+a.real,imag+a.imag); /*一般写法*/ /*complex b; b.real = a.real + real; b.imag = a.imag + imag; return b; */ }; void complex::display() const { cout << real << "+" << imag << "i" << endl; }; int main() { complex a(1.1,2.2); complex b(3.3,4.4); complex c; c = b + a; c.display(); return 0; }
当执行c=a+b时,编译器检测到”+”号左边(左结合性)是一个complex类对象时,就会调用该运算符重载函数,该语句被转换为:a.operator+(b)。
用全局函数重载运算符
运算符重载函数既可以声明为类的成员函数,也可以声明为所有类之外的全局函数。
声明为类的成员函数
此时二元运算符只有一个,一元运算符不需要参数。之所以少一个参数,是因为这个参数是隐含的,正如上面解释的,当执行c=a+b时,编译器检测到”+”号左边(左结合性)是一个complex类对象时,就会调用该运算符重载函数,该语句被转换为:a.operator+(b)。通过this指针隐式的访问a的成员变量。
声明为类之外的全局函数
将运算符重载函数声明为全局函数时,二元操作符就需要两个参数,一元操作符需要一个参数,而且其中必须有一个参数是对象,好让编译器区分这是程序员自定义的运算符,防止程序员修改用于内置类型的运算符的性质。
下面我们通过这种方法,实现复数的加减乘除。
/************************************************************************* > file name: 运算符重载.cpp > author: tanswer_ > mail: 98duxm@gmail.com > created time: 2016年10月15日 星期六 20时58分34秒 ************************************************************************/ #include #include #include #include #include #include #include #include #include #include using namespace std; class complex { private: double real; //实部 double imag; //虚部 public: complex(): real(0.0),imag(0.0) {} complex(double a,double b):real(a),imag(b) {} friend complex operator*(const complex & a,const complex & b); friend complex operator/(const complex & a,const complex & b); friend complex operator+(const complex & a,const complex & b); friend complex operator-(const complex & a,const complex & b); void display() const; }; /* + 运算符重载*/ complex operator+(const complex & a,const complex & b) { complex c; c.real = a.real + b.real; c.imag = a.imag + b.imag; return c; }; /* - 运算符重载*/ complex operator-(const complex & a,const complex & b) { complex c; c.real = a.real - b.real; c.imag = a.imag - b.imag; return c; }; /* * 运算符重载*/ complex operator*(const complex & a,const complex & b) { complex c; c.real = a.real * b.real - a.imag * b.imag; c.imag = a.imag * b.real + a.real * b.imag; return c; }; /* / 运算符重载*/ complex operator/(const complex & a,const complex & b) { complex c; double square = a.real*a.real + a.imag*a.imag; c.real = (a.real * b.real + a.imag * b.imag)/square; c.imag = (a.imag * b.real - a.real * b.imag)/square; return c; }; void complex::display() const { cout << real << "+" << imag << "i" << endl; }; int main() { complex a(1.1,2.2); complex b(3.3,4.4); complex c; c = a + b; cout << "a+b= "; c.display(); c = a - b; cout << "a-b= "; c.display(); c = a*b; cout << "a*b= "; c.display(); c = a/b; cout << "a/b= "; c.display(); return 0; }
输出结果:
a+b= 4.4 + 6.6i a-b= -2.2 + -2.2i a*b= -6.05 + 12.1i a/b= 2.2 + 0.4i
两点说明:
1. 因为要用到类complex的私有对象,所以将这几个全局函数声明为类的友元函数。
2. 指针操作符(->)、下标操作符([])、函数调用操作符(())、赋值操作符(=)只能以成员函数的形式重载。
重载<<和>>(输入和输出)
c++已经对”<<”和”>>”进行了重载,但是只能用于输入输出内置的数据类型。如果我们自己定义了一种新的数据类型,需要用输入输出去处理,那么就要重载。
下面我们用全局函数的形式重载输入运算符,使它读入两个double类型的数据,并转换为一个复数,保存到复数对象中。
istream & operator>>(istream &in, complex &a) { in >> a.real >> a.imag; return in; };
istream是输入流,cin就是istream类的对象。我们还是把它声明为类的友元函数:friend istream & operator>>(istream &in,complex &a);
在重载输入运算符时,采用引用的方式进行参数传递:输入的参数里面包含一个 istream 类的引用,返回值仍然为该引用。这样做的一个明显好处就是可以采用链式输入(也就是连续输入),如下所示:
complex a, b, c; cin>> a >> b >> c;
同样的,和输入类似,我们重载输出运算符,使其能够输出复数。
ostream & operator<<(ostream & out,complex &a) { out << a.real << " + " << a.imag << "i"; return out; };
这样我们就能实现对类的对象直接输入输出操作了。
#include using namespace std; class complex { private: double real; //实部 double imag; //虚部 public: complex(): real(0.0),imag(0.0) {} complex(double a,double b):real(a),imag(b) {} friend complex operator +(const complex & a,const complex &b); /*输入输出运算符重载的声明*/ friend istream & operator>>(istream &in,complex &a); friend ostream & operator<<(ostream &out,complex &a); friend complex operator+(const complex & a,const complex & b); friend complex operator-(const complex & a,const complex & b); }; /* + 运算符重载*/ complex operator+(const complex & a,const complex & b) { complex c; c.real = a.real + b.real; c.imag = a.imag + b.imag; return c; }; /* - 运算符重载*/ complex operator-(const complex & a,const complex & b) { complex c; c.real = a.real - b.real; c.imag = a.imag - b.imag; return c; }; istream & operator>>(istream &in, complex &a) { in >> a.real >> a.imag; return in; }; ostream & operator<<(ostream & out,complex &a) { out << a.real << " + " << a.imag << "i"; return out; }; int main() { complex a,b,c; cin >> a >> b; c = b + a; cout << "a+b= " << c << endl; c = a - b; cout << "a-b= " << c << endl; return 0; }
我们输入
1.1 2.2 3.3 4.4
输出:
a+b= 4.4 + 6.6i a-b= -2.2 + -2.2i
重载[] (下标运算符)
默认情况下使用下标运算符并不具备检查边界溢出功能,我们可以通过重载实现此功能。
/************************************************************************* > file name: 下标运算符重载.cpp > author: tanswer_ > mail: 98duxm@gmail.com > created time: 2016年10月16日 星期日 14时41分56秒 ************************************************************************/ #include #include #include #include #include #include #include #include #include #include using namespace std; class array { private: int length; int *num; public: array():length(0),num(null) {} array(int n); int getlength() const { return length; } int & operator[](int); const int & operator[](int) const; }; array::array(int n) { num = new int[n]; length = n; }; /*这个能够修改对象*/ int & array::operator[](int i) { if(i >= length || i < 0) throw string("out of bounds!"); return num[i]; }; /*常量成员函数,这个只能访问对象,不能修改*/ const int & array::operator[](int i) const { if(i >= length || i < 0) throw string("out of bounds!"); return num[i]; }; int main() { array a(5); int i; try{ for(i=0; i输出结果:
0 1 2 3 4 out of bounds!,i= 5();>
上面用到了string以及c++的异常处理,有疑问的可以跳过,重点是数组越界后产生异常了。a[5]会被转换为a.operator[](5)
重载=(赋值运算符)
对象之间也可以相互赋值,对象之间的赋值是将成员变量依次拷贝,而不是将整个对象的内存按位拷贝。一般情况下,默认的”=”就能满足我们的需求,但是当一个类中包含指针类型的成员变量时,可能会带来问题。
我们看下面的例子:
/************************************************************************* > file name: 重载赋值运算符.cpp > author: tanswer_ > mail: 98duxm@gmail.com > created time: 2016年10月20日 星期四 13时19分03秒 ************************************************************************/ #include #include #include #include #include #include #include #include #include #include using namespace std; class book { public: book():price(0.0),bookmark(null),num(0) {} book(double, int *,int); void setbookmark(int,int); //修改书签 void display(); private: double price; //书的价格 int *bookmark; //书签 int num; //书签的数量 }; book::book(double price,int *bm,int num):price(price),num(num) { //对象有自己的数组, //在其他地方修改实参所指向的数组不会影响该数组 int *bmtemp = new int[num]; for(int i=0; i bookmark = bmtemp; } void book::setbookmark(int page,int index) { if(index >= num-1) cout << "out of bound!" << endl; else bookmark[index] = page; } void book::display() { cout << "price:" << price <运行结果:
price:68.5 bookmarks: 1,49,56,290 price:68.5 bookmarks: 1,49,100,290book & book::operator=(const book & b) { if(this != &b) { this -> price = b.price; this -> num = b.num; //为bookmark赋值 int *bmtemp = new int[num]; for(int i=0; i bookmark = bmtemp; } return *this; };>;>
两次调用 display() 的结果不同表明,调用 java 对象的 setbookmark( ) 函数影响到了 cpp 对象。
上面程序中,当执行赋值语句时,将cpp.bookmark的值赋给java.bookmark,不同的对象的成员变量指向同一个数组,所以会相互影响。
要解决这个问题,我们就需要重载赋值运算符。
把重载函数放入book类中,再执行赋值语句,会转换为 java.operator=(cpp) 。在函数体中,this 就指向 java 对象。这样 java 对象也会拥有属于自己的数组,两个对象之间不会再相会影响。
下一篇: 学ASP应该注意ASP程序书写的规范