C++运算符重载

1. 一般运算符的重载

首先看一个复数类的例子

   1 class complex {
   2         double re, im;
   3 public:
   4         complex(double r, double i);
   5         complex add(complex b);
   6         complex multiply(complex b);
   7 };
   8 void f() {
   9     complex a(1.0, 1.0), b(-1.0, -2.0), c(0, 0);
  10     a = b.add(c); 
  11     b = b.add(c.multiply(a)); 
  12     c = a.multiply(b).add(complex(1,2)); 
  13     // a = b+c; 
  14     // b = b + c * a;
  15     // c = a * b + complex(1, 2);
  16 }

我们看到用成员函数的方式定义复数的加减乘除运算,调用起来非常不直观。我们希望复数类对象能够和int一样用+-*/运算符进行运算。这时就用到运算符重载,它为自定义类型添加运算符。

   1 class complex {
   2     double re, im;
   3 public:
   4     complex(double r, double i);
   5     complex  operator+ (complex  b);
   6     complex  operator* (complex  b);
   7 };
   8 int main() {
   9     complex a(2, 3), b(-1, 2), c(3, 4);
  10     a = b + c;   // b.operator+(c)
  11     b = b + c * a; // b.operator+(c.operator*(a))
  12     c = a * b + complex(1, 2); //a.operator*(b).operator+(complex(1,2)); 
  13 }
  14 complex complex::operator+(complex b) {
  15     return complex(re + b.re, im + b.im);
  16 }

能够重载的运算符

+     -    *    /    %    ^    &    |    ~    !    =    <    >
+=    -=    *=    /=    %=    ^=    &=    |=    <<    >>
>>=    <<=   ==    !=    <=    >=    &&    ||   ++    
--    ,    ->    [ ]     ()    new    new[ ]     delete    delete[ ]

不能重载的运算符

::   .    .*  ?:    sizeof    typeid

重载的运算符是一个名字和调用方法特殊的函数。可以用成员函数的方式重载,也可以用普通函数的方式重载,比如:

complex operator+( complex c, complex d);

重载的运算符的参数至少有一个是类,比如下面这些用法是错的:

int operator+(int a, int b); //error
bool operator==(char *, char *); // error

[ ], (), =, ->运算符必须以成员函数方式重载。运算符重载不会改变运算符原有的优先级、操作数数目、结合性。

   1 int main () {
   2    complex a, b, c;  
   3    complex d = a+b*c;
   4 }

不能够自己创造运算符,比如**。尽量不要改变运算符原来的语义,比如把加法操作定义成-,把减法操作定义成+。

二元运算符可以用带一个参数的成员函数的形式,也可以用带两个参数的普通函数的形式重载。区别:成员函数重载,第一个参数只能是类的对象。

   1 class X {
   2 public:
   3     void operator+( int i);
   4     X( int a );
   5 };
   6 void operator+( X a, X b);
   7 void operator+( X a, double b);
   8 void f( X a) {
   9     a+1;  // 相当于a.operator+(1);
  10     1+a;  //相当于 operator+(1, a);
  11     a+1.0;  //相当于 operator+(a, 1.0);
  12 }

一元运算符可以用不带参数的成员函数的形式,也可以用带一个参数的普通函数的形式重载

   1 class X {
   2         X* operator&(); // &a
   3         X* operator&(X b);  // 双目 a & b
   4         X operator++(); // ++a
   5 };
   6 X operator-(X); // -a
   7 X operator--(X); //--a
   8 void f( X a) {
   9     &a;  // 相当于a.operator&();
  10     -a;  //相当于 operator-(a);
  11     --a;  //相当于 operator--(a);
  12     ++a; //相当于 a.operator++();
  13 }

完整的complex运算符

   1 class complex {
   2     double re, im;
   3 public:
   4     complex( double r = 0, double i = 0): re(r), im (i) { }
   5     double& real() { return re; }
   6     double& imag() { return im; }
   7     complex operator+() { return *this; }
   8     complex operator-() { return complex(-re, -im); }
   9     complex operator+=( complex c) { re+= c.re; im+= c.im; return *this; }
  10     complex operator-=( complex c);
  11     complex operator*=( complex c);
  12     complex operator/=( complex c);
  13 };
  14 
  15 complex operator+( complex a, complex b) {
  16     a+=b; return a;
  17 }
  18 complex operator-( complex a, complex b);
  19 complex operator*( complex a, complex b);
  20 complex operator/( complex a, complex b);

练习:创建分数类,定义尽可能多的运算符

2. operator<<, operator>> 输入输出

<<>>是移位运算符,经常重载作输入输出用

   1 void f( complex c) {
   2     cout << " hello world" << 100;
   3     cout << c ;  // operator<<(cout, c)
   4 }
   5 ostream &operator<<(ostream&os, complex c) {
   6     return os << "(" << c.real() << "+" << c.imag() << "i)";
   7 }
   8 istream &operator>>(istream&is, complex &c) {
   9     return is >> c.real() >> c.imag();
  10 }

练习:输出输出Date类型的值

3. operator= 赋值

赋值运算符,自定义对象赋值行为

void f( complex c);

int main () {
    complex c1;
    complex c2(c1); //拷贝构造
    c2 = c1;
    f( c1 ); //拷贝构造
}

如果没有自定义赋值运算符,默认的赋值行为:类中逐个成员赋值。

   1 class string {
   2     char *str; 
   3     int size;
   4 public:
   5     string( char *s ) {
   6         size = strlen(s) +1;
   7         str = new char[size];
   8         strcpy(str, s);
   9     }
  10     ~string( ) { 
  11         delete [ ] str; 
  12     }
  13     string( const string& c){
  14         size = c.size;
  15         str = new char [size]; 
  16         strcpy(str, c.str); 
  17     } 
  18     string& operator=(const string&c){
  19         if(this != &c) {
  20             delete [ ] str; 
  21             size = c.size;
  22             str = new char[size]; 
  23             strcpy(str, c.str);
  24         } 
  25         return *this;
  26     }
  27 };
  28 
  29 int main() {
  30     string c ( "123" );
  31     string d ( c );
  32     c = d; // c.operator=(d)
  33 }

自定义赋值运算符加上自定义拷贝构造函数、析构函数,C++的内存管理就完整了。正确运用它们可以保证应用程序不产生内存泄漏。

练习:自己定义Stack类(栈),Array类(数组)

4. operator type 类型转换

可以将自定义类型转换为其他类型

   1 class string {
   2     char * str;
   3     int size;
   4 public:
   5     operator char *() {
   6         return str;
   7     }
   8 };

   1 class istream {
   2     bool flag;
   3 public:
   4     operator bool() {
   5         return flag;
   6     } 
   7     istream& operator>>(double);
   8 };
   9 extern istream cin;
  10 int main( ) {
  11     int i;
  12     while( cin >> i ) {
  13         /*...*/  
  14     }
  15 }

练习:自定义分数类型自动转换成浮点型

如何把内置类型自动转换成自定义类型?通过一个参数的构造函数,比如

   1 void f(complex c);
   2 int main() {
   3     complex c(5);
   4     complex d = 7;
   5     f(5);
   6 }
   7 
   8 class complex {
   9     double re, im;
  10 public:
  11     complex(double r) {
  12         re = r;
  13         im = 0;
  14     }
  15 };

如何禁止一个参数的构造函数自动转换类型?用explicit修饰构造函数

   1 class complex {
   2     double re, im;
   3 public:
   4     explicit complex(double r) {
   5         re = r;
   6         im = 0;
   7     }
   8 };

5. operator[] 数组取成员

让对象像数组一样使用

   1 class string {
   2     char *str;
   3     int size;
   4 public:
   5     char &operator[](int i) {
   6         return str[i];
   7     }
   8     char operator[](int i) const {
   9         return str[i];
  10     }
  11 };
  12 void f( string s) {
  13     cout << s[10]; //相当于s.operator[](10)
  14 }

operator[]运算符的参数可以是任何类型,但是只能是二元的

   1 class Dictionary {
   2 public:
   3     string &operator[](string word) {
   4         //...
   5     }
   6 };
   7 void f( Dictionary s) {
   8     cout << s[ "hello" ]; 
   9 }

练习:写一个Array类

6. operator() 函数调用

重载(),让对象可以像函数一样使用

   1 class Add {
   2     int base;
   3 public:
   4     Add(int b) : base(b) { }
   5     int operator()(int a) { return base + a; }
   6 };
   7 void f(Add add) {
   8     Add add2( 2 );
   9     int c = add2( 8 );  // 相当于 add2.operator()(8);
  10     c = add(5);
  11 }

operator()的参数个数可以任意。重载()的类又叫做仿函数,它是一种带状态的函数

7. operator++, operator-- 自增自减

++和--运算符分为前置和后置两种。

   1 class Integer {
   2     int i;
   3 public:
   4     Integer& operator++() {  //前置
   5         ++i;
   6         return *this;
   7     }
   8     Integer operator++(int) { //后置
   9         Integer temp(*this);
  10         i++;
  11         return temp;            
  12     }
  13 };
  14 void  f( Integer i ) {
  15     i++; // i.operator++(0);
  16     ++i; // i.operator++();
  17 }

练习: 写一个clock类

8. operator-> 指针取成员

重载->让一个对象可以像指针一样使用

   1 class autoptr{
   2     SchoolMember *person;
   3 public:
   4     autoptr( SchoolMember *p) : person(p) { }
   5     ~autoptr() { delete person; } 
   6     SchoolMember *operator->( ) { return person; }
   7     SchoolMember &operator*( ) { return *person; }
   8 };
   9 int main() {
  10     SchoolMember *s = new Teacher;
  11     s->print();
  12     delete s;
  13 
  14     autoptr p( new Teacher);
  15     p->print( );
  16 }

9. new, delete, new[ ], delete[ ] 内存分配

自定义对象new和delete的行为

   1 class memory{
   2 public:
   3     void *operator new(size_t  s) {
   4         return  malloc( s );
   5     }
   6     void operator delete(void *p) {
   7         free(p);
   8     }
   9 };
  10 int main() {
  11     memory *p = new memory; 
  12     // p = memory::new (sizeof(memory)); call constructor on p;
  13     delete p; 
  14     // p->~memory(); memory::operator delete(p);
  15 }

10. string例子

   1 class String{
   2     char *str; 
   3     int size;
   4 public:
   5     String(const char *s="");
   6     String(const String&s);
   7     String& operator=(const String&);
   8     virtual ~String();
   9     char &operator[ ](int i);
  10     char operator[ ](int i) const;
  11     bool operator==(const String &) const;
  12     int length() const;
  13     operator char*();
  14     friend ostream& operator<<(ostream&os, const String &s);
  15     friend istream& operator>>(istream&is, String &s);
  16 };


CategoryCpp

C++运算符重载 (2008-05-08 19:23:38由czk编辑)

ch3n2k.com | Copyright (c) 2004-2020 czk.