5406
备注:
|
9953
|
删除的内容标记成这样。 | 加入的内容标记成这样。 |
行号 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
[ ], (), =, ->运算符必须以成员函数方式重载。运算符重载不会改变运算符原有的优先级、操作数数目、结合性。
不能够自己创造运算符,比如**。尽量不要改变运算符原来的语义,比如把加法操作定义成-,把减法操作定义成+。
二元运算符可以用带一个参数的成员函数的形式,也可以用带两个参数的普通函数的形式重载。区别:成员函数重载,第一个参数只能是类的对象。
一元运算符可以用不带参数的成员函数的形式,也可以用带一个参数的普通函数的形式重载
完整的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>> 输入输出
<<和>>是移位运算符,经常重载作输入输出用
练习:输出输出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 类型转换
可以将自定义类型转换为其他类型
练习:自定义分数类型自动转换成浮点型
如何把内置类型自动转换成自定义类型?通过一个参数的构造函数,比如
如何禁止一个参数的构造函数自动转换类型?用explicit修饰构造函数
5. operator[] 数组取成员
让对象像数组一样使用
operator[]运算符的参数可以是任何类型,但是只能是二元的
练习:写一个Array类
6. operator() 函数调用
重载(),让对象可以像函数一样使用
operator()的参数个数可以任意。重载()的类又叫做仿函数,它是一种带状态的函数
7. operator++, operator-- 自增自减
++和--运算符分为前置和后置两种。
练习: 写一个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 };