版本2和6间的区别 (跳过第4版)
于2006-12-06 20:53:58修订的的版本2
大小: 5406
编辑: czk
备注:
于2006-12-20 20:53:36修订的的版本6
大小: 9953
编辑: czk
备注:
删除的内容标记成这样。 加入的内容标记成这样。
行号 4: 行号 4:
复数类 首先看一个复数类的例子
行号 24: 行号 24:
运算符重载:为类型添加运算符 我们看到用成员函数的方式定义复数的加减乘除运算,调用起来非常不直观。我们希望复数类对象能够和int一样用+-*/运算符进行运算。这时就用到运算符重载,它为自定义类型添加运算符。
行号 57: 行号 57:
重载的运算符是一个名字和调用方法特殊的函数。可以用成员函数的方式重载,也可以用普通函数的方式重载 重载的运算符是一个名字和调用方法特殊的'''函数'''。可以用成员函数的方式重载,也可以用普通函数的方式重载,比如:
行号 61: 行号 61:
重载的运算符的参数至少有一个是类
重载的运算符的参数至少有一个是类,比如下面这些用法是错的:
行号 67: 行号 68:
[ ], (), =, ->运算符必须以成员函数方式重载。运算符重载不会改变运算符原有的优先级、操作数数目、结合性。
{{{
complex a, b, c;
a+b*c;
}}}
不能够自己创造运算符,比如**。尽量不要改变运算符原来的语义。
{{{[ ], (), =, ->}}}运算符必须以成员函数方式重载。运算符重载不会改变运算符原有的优先级、操作数数目、结合性。
{{{#!cplusplus
int main () {
   
complex a, b, c;
   complex d = a+b*c;
}
}}}

不能够自己创造运算符,比如**。尽量不要改变运算符原来的语义,比如把加法操作定义成-,把减法操作定义成+
行号 91: 行号 94:
单目运算符可以用不带参数的成员函数的形式,也可以用带一个参数的普通函数的形式重载 一元运算符可以用不带参数的成员函数的形式,也可以用带一个参数的普通函数的形式重载
行号 136: 行号 139:
移位运算符,经常重载作输入输出用 <<和>>是移位运算符,经常重载作输入输出用
行号 139: 行号 142:
    cout << hello world << 100;     cout << " hello world" << 100;
行号 143: 行号 146:
    return os << ( << c.real() << + << c.imag() << i);     return os << "(" << c.real() << "+" << c.imag() << "i)";
行号 150: 行号 153:
行号 153: 行号 157:
Class complex;
complex c1;
complex c2(c1); //拷贝构造
c2 = c1;
行号 158: 行号 158:
f( c1 ); //拷贝构造
}}}

int main () {
    complex c1;
    complex c2(c1); //拷贝构造
    c2 = c1;
    f( c1 ); //拷贝构造
}
}}}
行号 161: 行号 168:
{{{#!cplusplus
行号 190: 行号 198:
    string c ( 123 );     string c ( "123" );
行号 196: 行号 204:
自定义赋值运算符加上自定义拷贝构造函数、析构函数,C++的内存管理就完整了。正确运用它们可以保证应用程序不产生内存泄漏。

练习:自己定义Stack类(栈),Array类(数组)
行号 197: 行号 209:
== operator[ ] 数组取成员 == 可以将自定义类型转换为其他类型
{{{#!cplusplus
class string {
    char * str;
    int size;
public:
    operator char *() {
        return str;
    }
};
}}}

{{{#!cplusplus
class istream {
    bool flag;
public:
    operator bool() {
        return flag;
    }
    istream& operator>>(double);
};
extern istream cin;
int main( ) {
    int i;
    while( cin >> i ) {
        /*...*/
    }
}
}}}

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

如何把内置类型自动转换成自定义类型?通过一个参数的构造函数,比如
{{{#!cplusplus
void f(complex c);
int main() {
    complex c(5);
    complex d = 7;
    f(5);
}

class complex {
    double re, im;
public:
    complex(double r) {
        re = r;
        im = 0;
    }
};
}}}

如何禁止一个参数的构造函数自动转换类型?用explicit修饰构造函数
{{{#!cplusplus
class complex {
    double re, im;
public:
    explicit complex(double r) {
        re = r;
        im = 0;
    }
};
}}}

== operator[] 数组取成员 ==
让对象像数组一样使用
{{{#!cplusplus
class string {
    char *str;
    int size;
public:
    char &operator[](int i) {
        return str[i];
    }
    char operator[](int i) const {
        return str[i];
    }
};
void f( string s) {
    cout << s[10]; //相当于s.operator[](10)
}
}}}

operator[]运算符的参数可以是任何类型,但是只能是二元的
{{{#!cplusplus
class Dictionary {
public:
    string &operator[](string word) {
        //...
    }
};
void f( Dictionary s) {
    cout << s[ "hello" ];
}
}}}
练习:写一个Array类
行号 199: 行号 306:

重载(),让对象可以像函数一样使用
{{{#!cplusplus
class Add {
    int base;
public:
    Add(int b) : base(b) { }
    int operator()(int a) { return b + a; }
};
void f(Add add) {
    Add add2( 2 );
    int c = add2( 8 ); // 相当于 add2.operator()(8);
    c = add(5);
}
}}}
operator()的参数个数可以任意。重载()的类又叫做仿函数,它是一种带状态的函数
行号 200: 行号 324:
++和--运算符分为前置和后置两种。
{{{#!cplusplus
class Integer {
    int i;
public:
    Integer& operator++() { //前置
        ++i;
        return *this;
    }
    Integer operator++(int) { //后置
        Integer temp(*this);
        i++;
        return temp;
    }
};
void f( Integer i ) {
    i++; // i.operator++(0);
    ++i; // i.operator++();
}
}}}
练习: 写一个clock类
行号 201: 行号 347:

重载->让一个对象可以像指针一样使用
{{{#!cplusplus
class autoptr{
    SchoolMember *person;
public:
    autoptr( SchoolMember *p) : person(p) { }
    ~autoptr() { delete person; }
    SchoolMember *operator->( ) { return person; }
    SchoolMember &operator*( ) { return *person; }
};
int main() {
    SchoolMember *s = new Teacher;
    s->print();
    delete s;

    autoptr p( new Teacher);
    p->print( );
}
}}}
行号 202: 行号 369:
自定义对象new和delete的行为
{{{#!cplusplus
class memory{
public:
    void *operator new(size_t s) {
        return malloc( s );
    }
    void operator delete(void *p) {
        free(p);
    }
};
int main() {
    memory *p = new memory;
    // p = memory::new (sizeof(memory)); call constructor on p;
    delete p;
    // p->~memory(); memory::operator delete(p);
}
}}}

== string例子 ==
{{{#!cplusplus
class String{
    char *str;
    int size;
public:
    String(const char *s="");
    String(const String&s);
    String& operator=(const String&);
    virtual ~String();
    char &operator[ ](int i);
    char operator[ ](int i) const;
    bool operator==(const String &) const;
    int length() const;
    operator char*();
    friend ostream& operator<<(ostream&os, const String &s);
    friend istream& operator>>(istream&is, String &s);
};
}}}

= The End =

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 b + 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 };

The End

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

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