当前位置:   article > 正文

C++面向对象编程之六:重载操作符(<<,>>,+,+=,==,!=,=)_c++重载<<

c++重载<<
重载操作符

C++允许我们重新定义操作符(例如:+,-,*,/)等,使其对于我们自定义的类类型对象,也能像内置数据类型(例如:int,float,double)等一样直观,可以进行加,减,乘,除,比较大小等等操作。

重载操作符本质是函数,只是这个函数的函数名比较特别,为:operator后接需要重新定义的操作符的符号。例如,重载+号,函数名为:operator+;重载-号,函数名:operator-。因为重载操作符本质是函数,所以实际上就是为某个自定义的数据类类型或枚举类型实现函数重载,比如内置int类型已经有

int operator+(int, int)版本,我们有一个自定义的complex(复数类),要想跟内置的int类型有一样直观的操作两个复数相加,只要我们为自定义的complex(复数类)重定义操作符+,提供complex operator+(complex, complex)版本即可。

重载操作符语法
  1. 返回值类型 operator操作符号(形参列表)
  2. {
  3. }

重载操作符的两种形式

对于大多数重载操作符来说,可以定义为全局函数或类的成员函数。

1.重载操作符为全局函数,并且通常必须将这个全局函数设置为所操作类的友元

  1. #include <iostream>
  2. using namespace std;
  3. class MyInt
  4. {
  5. friend MyInt operator+(const MyInt &a, const MyInt &b); //这个函数为MyInt类的友元
  6. public:
  7. MyInt():m_num(0)
  8. {
  9. }
  10. MyInt(const int num):m_num(num)
  11. {
  12. }
  13. int getNum() const
  14. {
  15. return m_num;
  16. }
  17. private:
  18. int m_num;
  19. };
  20. //重载操作符为全局函数
  21. MyInt operator+(const MyInt &a, const MyInt &b)
  22. {
  23. MyInt temp;
  24. temp.m_num = a.m_num + b.m_num; //如果没有把这个全局函数设置为操作类MyInt的友元,则不能直接访问MyInt类的
  25. //私有成员变量m_num
  26. return temp;
  27. }
  28. int main(int argc, char *argv[])
  29. {
  30. MyInt a(1);
  31. MyInt b(2);
  32. MyInt c = a + b; //本质是:调用了全局函数,即 MyInt c = operator+(a, b);
  33. MyInt d = operator+(a, b);
  34. cout << "a + b = " << c.getNum() << endl;
  35. cout << "operator+(a, b) = " << d.getNum() << endl;
  36. return 0;
  37. }

注意:重载操作符为全局函数时,本质是调用了该全局函数。例如:MyInt c = a + b; //本质是:调用了全局函数,即 MyInt c = operator+(a, b);

2.重载操作符为类的成员函数

  1. #include <iostream>
  2. using namespace std;
  3. class MyInt
  4. {
  5. public:
  6. MyInt():m_num(0)
  7. {
  8. }
  9. MyInt(const int num):m_num(num)
  10. {
  11. }
  12. int getNum() const
  13. {
  14. return m_num;
  15. }
  16. MyInt operator+(const MyInt &b) //重载操作符为类成员函数
  17. {
  18. MyInt temp;
  19. temp.m_num = this->m_num + b.m_num;
  20. return temp;
  21. }
  22. private:
  23. int m_num;
  24. };
  25. int main(int argc, char *argv[])
  26. {
  27. MyInt a(1);
  28. MyInt b(2);
  29. MyInt c = a + b; //本质是:调用了MyInt类的成员函数,即 MyInt c = a.operator+(b);
  30. MyInt d = a.operator+(b);
  31. cout << "a + b = " << c.getNum() << endl;
  32. cout << "a.operator+(b) = " << d.getNum() << endl;
  33. return 0;
  34. }

注意:重载操作符为类的成员函数时,本质是调用了该类的成员函数。例如:MyInt c = a + b; //本质是:调用了MyInt类的成员函数,即 MyInt c = a.operator+(b);

重载操作符为全局函数和重载操作符为类的成员函数的形参区别

1.对于重载操作符为全局函数和重载操作符为类的成员函数,形参的个数看上去是有区别的,一般重载操作符为类的成员函数,其形参个数看起来比重载操作符为全局函数的形参个数少1个,实际上,重载操作符为类的成员函数,这个成员函数有一个隐含的this指针形参,限定为操作符的第一个操作数,所以对于编译器而言。形参个数并没有少1个的。而重载操作符为全局函数,这个全局函数的第一个形参则为操作符的第一个操作数

2.对于重载操作符函数,形参是有顺序的,例如:cout << "Hello World\n";这句是正常的输出操作符<< 打印的语法:第一个操作数的实参为:cout,第二个操作数的实参为:"Hello World\n",那么正确的全局函数定义为:ostream& operator<<(ostream& cout, const string &str);但是如果我们将全局函数定义为:ostream& operator<<(const string &strostream& cout);函数体内我们同样实现功能正常打印,我们用函数调用法:operator("Hello World\n", cout);也能在屏幕中正确打印:Hello World。看起来也没有错。但简化的调用版本形参是有顺序的,第一操作数为函数的第一个形参,第二个操作数为函数的第二个形参。所以简化的调用版本为:"Hello World\n" << cout;这就是错误的了,所以对于重载操作符函数,形参是有顺序的

重载操作符为全局函数还是重载操作符为类的成员函数,应该怎么选择?

对于重载操作符可以为一个普通的全局函数或一个类的成员函数这两种方式来实现,我的建议是,对于能够使用类的成员函数来实现的,我们应该选择用类的成员函数来实现,因为选择用普通的全局函数来实现,需要把这个全局函数设置为操作类的友元,从而达到可以直接访问该操作类的非共有成员变量的目的,但这破坏类的封装性。

不能重载的操作符
  1. .       :类的对象访问操作符
  2. .* ->*   :类的对象或或类的对象指针访问类中的函数指针操作符
  3. ::        :域操作符
  4. ?= :条件操作符
  5. sizeof    :长度操作符
  6. #        :预处理操作符        
重载操作符可以给我们自定义的类类型对象的进行操作符运算的时候更加直观。但不要滥用重载操作符。

1.不能改变内置类型的操作符的含义。例如:int operator+(int, int)

2.不能为内置类型定义额外的新的操作符。例如:不能定义两个数组为操作数的operator+

3.逗号(,),取地址(&),逻辑与(&&),逻辑或(||)这些操作符具有有用的内置含义,不建议重载。

假设我们有一个MyInt类,实现的功能跟内置的类型int一样。那么下面我们为这个MyInt类实现一下相关的操作符重载吧。

重载输出操作符<<

分析:

  1. 对于内置的类型int
  2. int a = 0;
  3. cout << a << endl;
  4. << 操作符有两个操作数,
  5. 第一个(左)操作数数据类型为:ostream&,
  6. 第二个(右)操作数数据类型为:const int&
  7. 所以我们只能重载输出操作符为全局函数,不能为MyInt类的成员函数,
  8. 因为重载操作符为类的成员函数的第一个操作数数据类型限定为this指针,
  9. 而我们需要的是第一个操作数数据类型为:ostream&,
  10. 因为输出a后,后面的操作是还能输出换行的,所以返回值类型为ostream&

经过以上分析,我们可以得出,重载输出操作符<<的全局函数为:

ostream& operator<<(ostream &cout, const MyInt a)

  1. #include <iostream>
  2. using namespace std;
  3. class MyInt
  4. {
  5. friend ostream& operator<<(ostream &cout, const MyInt &a);
  6. public:
  7. MyInt():m_num(0)
  8. {
  9. }
  10. MyInt(const int num):m_num(num)
  11. {
  12. }
  13. private:
  14. int m_num;
  15. };
  16. ostream& operator<<(ostream &cout, const MyInt &a)
  17. {
  18. cout << a.m_num;
  19. return cout;
  20. }
  21. int main(int argc, char *argv[])
  22. {
  23. MyInt a;
  24. cout << a << endl;
  25. return 0;
  26. }

重载输入操作符>>

分析:

  1. 对于内置的类型int
  2. int a = 0;
  3. int b = 0;
  4. cin >> a >> b;
  5. >> 操作符有两个操作数,
  6. 第一个(左)操作数数据类型为:istream&,
  7. 第二个(右)操作数数据类型为:const int&
  8. 所以我们只能重载输出操作符为全局函数,不能为MyInt类的成员函数,
  9. 因为重载操作符为类的成员函数的第一个操作数数据类型限定为this指针,
  10. 而我们需要的是第一个操作数数据类型为:istream&,
  11. 因为输入a后,后面的操作是还能输入b的,所以返回值类型为istream&

经过以上分析,我们可以得出,重载输入操作符>>的全局函数为:

istream& operator>>(istream &cin, MyInt &a)

  1. #include <iostream>
  2. using namespace std;
  3. class MyInt
  4. {
  5. friend ostream& operator<<(ostream &cout, const MyInt &a);
  6. friend istream& operator>>(istream &cin, MyInt &a);
  7. public:
  8. MyInt():m_num(0)
  9. {
  10. }
  11. MyInt(const int num):m_num(num)
  12. {
  13. }
  14. private:
  15. int m_num;
  16. };
  17. ostream& operator<<(ostream &cout, const MyInt &a)
  18. {
  19. cout << a.m_num;
  20. return cout;
  21. }
  22. istream& operator>>(istream &cin, MyInt &a)
  23. {
  24. cin >> a.m_num;
  25. return cin;
  26. }
  27. int main(int argc, char *argv[])
  28. {
  29. MyInt a;
  30. MyInt b;
  31. cout << "请输入a和b的值,用空格隔开:" << endl;
  32. cin >> a >> b;
  33. cout << "a = " << a << ",b = " << b << endl;
  34. return 0;
  35. }
算术运算符和关系运算符

一般来说,我们应该将算术操作符和关系操作符定义为非成员函数。这是因为,算术操作符和关系操作符中,例如:有左操作数为int a = 1,右操作数为double b = 2.12345,进行 a + b运算时,左操作数a会转成跟右操作数b的精度一样的double类型,即:a + b = 1.000000000000000(双精度保留15位小数)+ 2.123450000000000(双精度保留15位小数),如果将算术操作符和关系操作符定义定义为类的成员函数时,左操作数限定为隐含的this指针,就违背了在C/C++中,如果一个表达式中含有不同类型的常量或变量,在计算时,会将它们自动转换为精度更高的同一种数据类型。

重载相加操作符+

1.返回值类型为:MyInt,因为两个数相加,是不能改变左操作数或右操作数的值的,所以返回值类型为MyInt,而不是MyInt&

2.重载相加操作符+的非成员函数为:MyInt operator+(const MyInt &a, const MyInt &b)

  1. #include <iostream>
  2. using namespace std;
  3. class MyInt
  4. {
  5. friend ostream& operator<<(ostream &cout, const MyInt &a);
  6. friend istream& operator>>(istream &cin, MyInt &a);
  7. friend MyInt operator+(const MyInt &a, const MyInt &b);
  8. public:
  9. MyInt():m_num(0)
  10. {
  11. }
  12. MyInt(const int num):m_num(num)
  13. {
  14. }
  15. private:
  16. int m_num;
  17. };
  18. ostream& operator<<(ostream &cout, const MyInt &a)
  19. {
  20. cout << a.m_num;
  21. return cout;
  22. }
  23. istream& operator>>(istream &cin, MyInt &a)
  24. {
  25. cin >> a.m_num;
  26. return cin;
  27. }
  28. MyInt operator+(const MyInt &a, const MyInt &b)
  29. {
  30. return MyInt(a.m_num + b.m_num);
  31. }
  32. int main(int argc, char *argv[])
  33. {
  34. MyInt a(1);
  35. MyInt b(2);
  36. cout << "a + b = " << a + b << endl;
  37. return 0;
  38. }
重载复合赋值操作符+=

1.返回值类型为:MyInt&,因为x += y;是要改变左操作数x,并且返回的还是被修改后的x的

2.重载复合赋值操作符+=的非成员函数为:MyInt& operator+=(MyInt &a, const MyInt &b)

  1. #include <iostream>
  2. using namespace std;
  3. class MyInt
  4. {
  5. friend ostream& operator<<(ostream &cout, const MyInt &a);
  6. friend istream& operator>>(istream &cin, MyInt &a);
  7. friend MyInt& operator+=(MyInt &a, const MyInt &b);
  8. public:
  9. MyInt():m_num(0)
  10. {
  11. }
  12. MyInt(const int num):m_num(num)
  13. {
  14. }
  15. private:
  16. int m_num;
  17. };
  18. ostream& operator<<(ostream &cout, const MyInt &a)
  19. {
  20. cout << a.m_num;
  21. return cout;
  22. }
  23. istream& operator>>(istream &cin, MyInt &a)
  24. {
  25. cin >> a.m_num;
  26. return cin;
  27. }
  28. MyInt& operator+=(MyInt &a, const MyInt &b)
  29. {
  30. a.m_num += b.m_num;
  31. return a;
  32. }
  33. int main(int argc, char *argv[])
  34. {
  35. MyInt a(1);
  36. MyInt b(2);
  37. a += b;
  38. cout << "after a += b, a = " << a << endl;
  39. return 0;
  40. }
重载相等操作符==,不等操作符-=

1.如果类中有一个操作,是确认该类中的两个对象是否相等的,我们应该将该函数名定义为operator==,而不是定义一个命名函数。因为我们更习惯于用==操作符来比较两个对象是否相等。

2.对于一个类,如果我们定义了operator==,也应该定义operator!=。

3.定义了operator==的类更容易与标准库一起使用。例如:find,默认使用了==操作符,如果定义了==,那么这么算法可以无须任何特殊处理而用于该类类型。

  1. #include <iostream>
  2. using namespace std;
  3. class MyInt
  4. {
  5. friend ostream& operator<<(ostream &cout, const MyInt &a);
  6. friend istream& operator>>(istream &cin, MyInt &a);
  7. friend bool operator==(const MyInt &a, const MyInt &b);
  8. friend bool operator!=(const MyInt &a, const MyInt &b);
  9. public:
  10. MyInt():m_num(0)
  11. {
  12. }
  13. MyInt(const int num):m_num(num)
  14. {
  15. }
  16. private:
  17. int m_num;
  18. };
  19. ostream& operator<<(ostream &cout, const MyInt &a)
  20. {
  21. cout << a.m_num;
  22. return cout;
  23. }
  24. istream& operator>>(istream &cin, MyInt &a)
  25. {
  26. cin >> a.m_num;
  27. return cin;
  28. }
  29. bool operator==(const MyInt &a, const MyInt &b)
  30. {
  31. return a.m_num == b.m_num;
  32. }
  33. bool operator!=(const MyInt &a, const MyInt &b)
  34. {
  35. return a.m_num != b.m_num;
  36. }
  37. int main(int argc, char *argv[])
  38. {
  39. MyInt a(1);
  40. MyInt b(2);
  41. MyInt c(1);
  42. if (a == b)
  43. cout << "a == b" << endl;
  44. else
  45. cout << "a != b" << endl;
  46. if (a == c)
  47. cout << "a == c" << endl;
  48. else
  49. cout << "a != c" << endl;
  50. if (a != b)
  51. cout << "a != b" << endl;
  52. else
  53. cout << "a == b" << endl;
  54. if (a != c)
  55. cout << "a != c" << endl;
  56. else
  57. cout << "a == c" << endl;
  58. return 0;
  59. }
重载赋值操作符=

1.C++是允许类类型对象给同类型的其他对象赋值的,具体可查看我写的这篇文章里利用等号法创建对象的介绍C++面向对象编程之二:构造函数、拷贝构造函数、析构函数,这里不再累赘。类赋值操作符形参的数据类型为该类类型,通常形参为该类类型的const引用,或该类的非const引用或类类型或更多的其他数据类型(例如:MyInt类中,那么该类的赋值操作符形参可为const MyInt &,或MyInt &或MyInt)。如果我们没有定义赋值操作符=,那么编译器将为该类提供一个。所以,类赋值操作符=必须是类成员函数,以便编译器可以知道是否需要为该类提供一个。

2.重载赋值操作符=的返回值类型必须是*this的引用(即左操作符的引用),这样子返回是跟内置数据类型的赋值是一致的。因为赋值返回*this的引用,这样子就不需要创建一个该类的一个临时对象,然后将临时对象返回,然后再调用该类的拷贝构造函数,将该临时对象的值拷贝后,又释放该临时对象,这一系列的系统开销,从而提高代码的执行效率。

  1. #include <iostream>
  2. using namespace std;
  3. class MyInt
  4. {
  5. friend ostream& operator<<(ostream &cout, const MyInt &a);
  6. friend istream& operator>>(istream &cin, MyInt &a);
  7. public:
  8. MyInt():m_num(0)
  9. {
  10. }
  11. MyInt(const int num):m_num(num)
  12. {
  13. }
  14. MyInt& operator=(const MyInt &b)
  15. {
  16. m_num = b.m_num;
  17. return *this;
  18. }
  19. private:
  20. int m_num;
  21. };
  22. ostream& operator<<(ostream &cout, const MyInt &a)
  23. {
  24. cout << a.m_num;
  25. return cout;
  26. }
  27. istream& operator>>(istream &cin, MyInt &a)
  28. {
  29. cin >> a.m_num;
  30. return cin;
  31. }
  32. int main(int argc, char *argv[])
  33. {
  34. MyInt a(1);
  35. MyInt b = a;
  36. cout << "a = " << a << endl;
  37. cout << "b = " << b << endl;
  38. return 0;
  39. }

好了,关于重载操作符,篇幅比较长,本文对重载操作符(<<,>>,+,+=,==,!=,=)进行了讲解,还有一些比较重要的操作符,比如前++,后++,前--,后--,下标操作符[]等,还没有介绍,就放到下一篇博文吧!

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号