【C++学习笔记】运算符重载

Myth丶恋晨 2022-10-13 02:58 307阅读 0赞

文章目录

  • 0x00 前言
  • 0x01 运算符重载
  • 0x02 运算符重载规则
  • 0x03 运算符重载为成员函数
  • 0x04 运算符重载为非成员函数

0x00 前言

文章中的文字可能存在语法错误以及标点错误,请谅解;

如果在文章中发现代码错误或其它问题请告知,感谢!

本文档为个人边学习边记录的C++笔记,非教程,笔记中会存在引用他人文章内容的部分,被引用的原文不会被特殊标记出来,但会在参考文档中给出原文链接。

0x01 运算符重载

运算符重载是对已有的运算符赋予多重含义,使同一运算符作用于不同类型数据时导致不同行为。

运算符重载的实质就是函数重载。在实现过程中,首先把指定的运算表达式转化为对运算符函数的调用,将运算对象转化为运算符函数的实参,然后根据实参的类型来确定需要调用的函数,这个过程是在编译过程中完成的。

0x02 运算符重载规则

运算符重载规则如下:
(1)C++中的运算符处理少数几个之外,全部都可以重载,而且只能重载C++已经有的运算符。
(2)重载之后的运算符的优先级和结合性都不会改变。
(3)运算符重载是针对新类型数据的实际需要,对原有运算符进行适当的改造。一般来讲,重载的功能应当与原有功能相似,不能改变元运算符的操作对象个数,同时至少要有一个操作对象是自定义类型。

运算符的重载形式有两种,即重载为类的非静态成员函数重载为非成员函数

运算符重载为类的成员函数一般的语法形式为:

  1. 返回类型 operator 运算符(形参表)
  2. {
  3. 函数体
  4. }

运算符重载为类的非成员函数一般的语法形式为:

  1. 返回类型 operator 运算符(形参表)
  2. {
  3. 函数体
  4. }

返回类型指定了重载运算符的返回值类型,也就是运算结果类型;operator是定义运算符重载函数的关键字;运算符即是要重载的运算符名称,必须是C++中科重载运算符,比如要重载加法运算符,这里就写“+”;形参表中给出重在运算符所需要的参数和类型。

当以非成员函数形式重载运算符时,有时需要访问运算符参数所涉及类的私有成员,这时可以把该函数声明为类的友元函数。

当运算符重载为类的成员函数时,函数的参数个数比原来的操作数个数要少一个(后置“++”,“–”除外);当重载函数为非成员函数时,参数个数与原操作数个数相同。两种情况的参数个数有所差异的原因是,重载为类的成员函数时,第一个操作数会被作为函数调用的目的对象,因此无需出现在参数表中,函数体中可以直接访问第一个操作数的成员;而重载为非成员函数时,运算符的所有操作数必须显式通过参数传递。

运算符重载的主要优点就是可以改变现有运算符的操作方式,以用于类类型,使得程序看起来更加直观。

0x03 运算符重载为成员函数

运算符重载实质上就是函数重载,重载为成员函数,它就可以自由的访问本类数据成员。

如果是双目运算符,左操作数就是对象本身的数据,由this指针指出,右操作数则需要通过运算符重载函数的参数表来传递;如果是单目运算符,操作数由对象的this指针给出,就不再需要任何参数。

对于双目运算符B,如果要重载为类的成员函数,使之能够实现表达式oprd1 B oprd2,其中oprd1为A类的对象,则应当把B重载为A类的成员函数,该函数只有一个形参,形参的类型是oprd2所属的类型。经过重载之后,表达式oprd1 B oprd2就相当于函数调用oprd1.operatorB(oprd2).

对于前置单目运算符U,如“-”(负号)等,如果要重载为类的成员函数,用来实现表达式U oprd,其中oprd为A类的对象,则U应当重载为A类的成员函数,函数没有形参。经过重载之后,表达式U oprd相当于函数调用oped.operator U()。

后置运算符“++”和“–“,如果将它们重载为类的成员函数,用来实现表达式oprd++或oprd–,其中oprd为A类对象,那么运算符就应当重载为A类的成员函数,这时函数要带有一个整型(int)形参。重载之后,表达式oprd++和oprd–就相当于函数调用oprd.operator++(0)和oprd.operator–(0).这里的int类型参数在运算中不起任何作用,只是用于区别后置++、–与前置++、–。

在UML语言中,重载的运算符表示方法与其他成员函数类似,其形式为”operator 运算符(形参表):函数类型“。

例:复数类加减法运算重载为成员函数成员。
在这里插入图片描述

带有加减法运算符重载的复数类的UML图形表示

  1. #include<iostream>
  2. using namespace std;
  3. class Complex{
  4. public:
  5. Complex(double r = 0.0, double i = 0.0):real(r), imag(i){ }
  6. Complex operator+(const Complex &c2) const;
  7. Complex operator-(const Complex &c2) const;
  8. void display() const;
  9. private:
  10. double real;
  11. double imag;
  12. };
  13. Complex Complex::operator+(const Complex &c2) const{
  14. return Complex(real + c2.real, imag + c2.imag);
  15. }
  16. Complex Complex::operator-(const Complex &c2) const{
  17. return Complex(real - c2.real, imag - c2.imag);
  18. }
  19. void Complex::display() const{
  20. cout<<"("<<real<<","<<imag<<")"<<endl;
  21. }
  22. int main(){
  23. Complex c1(5, 4), c2(2, 10), c3;
  24. cout<<"c1 = ";c1.display();
  25. cout<<"c2 = ";c2.display();
  26. c3 = c1 - c2;
  27. cout<<"c3 = c1 - c2 = ";c3.display();
  28. c3 = c1 + c2;
  29. cout<<"c3 = c1 + c2 = ";c3.display();
  30. return 0;
  31. }

运行结果:

  1. c1 = (5,4)
  2. c2 = (2,10)
  3. c3 = c1 - c2 = (3,-6)
  4. c3 = c1 + c2 = (7,14)

例:将单目运算符“++”重载为成员函数形式

  1. #include<iostream>
  2. using namespace std;
  3. class Clock{
  4. public:
  5. Clock(int hour = 0, int minute = 0, int second = 0);
  6. void showTime() const;
  7. Clock & operator++(); //前置单目运算符重载
  8. Clock operator++(int); //后置单目运算符重载
  9. private:
  10. int hour, minute, second;
  11. };
  12. Clock::Clock(int hour, int minute, int second){
  13. if(0 <= hour && hour < 24 && 0 <= minute && minute < 60
  14. && 0 <= second && second < 60){
  15. this->hour = hour;
  16. this->minute = minute;
  17. this->second = second;
  18. }else
  19. cout << "Time error!" << endl;
  20. }
  21. void Clock::showTime() const{
  22. cout << hour << ":" << minute << ":" << second << endl;
  23. }
  24. Clock &Clock::operator++(){ //前置单目运算符重载函数
  25. second++;
  26. if(second >= 60){
  27. second -= 60;
  28. minute++;
  29. if(minute >= 60){
  30. minute -= 60;
  31. hour = (hour + 1) % 24;
  32. }
  33. }
  34. return *this;
  35. }
  36. Clock Clock::operator++(int){ //后置单目运算符重载函数
  37. Clock old = *this;
  38. ++(*this);
  39. return old;
  40. }
  41. int main(){
  42. Clock myClock(23, 59, 59);
  43. cout << "First time output" << endl;
  44. myClock.showTime();
  45. cout << "Show myClock++:";
  46. (myClock++).showTime();
  47. cout << "Show++myClock:";
  48. (++myClock).showTime();
  49. return 0;
  50. }

运行结果:

  1. First time output
  2. 23:59:59
  3. Show myClock++:23:59:59
  4. Show++myClock:0:0:1

前置单目运算符和后置单目运算符的重载最主要的区别在于重载函数的形参。语法规定,前置单目运算符重载为成员函数时没有形参,而后置单目运算符重载为成员函数时需要有一个int型形参。

0x04 运算符重载为非成员函数

运算符也可以重载为非成员函数。这时,运算所需要的操作数都需要通过函数的形参表来传递,在形参表从左到右的顺序就是运算符操作数的顺序。如果需要访问运算符参数对象的私有成员,可以将该函数声明为类的友元类。

对于双目运算符B,如果要实现oprd1 B oprd2,其中oprd1和oprd2中只要有一个具有自定义类型,就可以将B重载为非成员函数,函数的形参为oprd1和oprd2.经过重载之后,表达式oprd1 B oprd2就相当于函数调用operator B(oprd1, oprd2)。

对于前置单目运算符U,如“-”(负号)等,如果要实现表达式U oprd,其中oprd具有自定义类型,就可以将U重载为非成员函数,函数的形参为oprd。经过重载之后,表达式U oprd相当于函数调用operator U(oprd)。

对于后置运算符++和–,如果要实现表达式oprd++或oprd–,其中oprd为自定义类型,那么运算符就可以重载为非成员函数。这时函数的形参有两个,一个oprd,另一个时int类型形参。第二个参数是用于与前置运算符函数相区别的。重载之后,表达式oprd++和oprd–就相当于函数调用operator++(oprd,0)和operator–(oprd,0)。

例:以非成员函数形式重载Complex的加减运算和“<<”运算符。
在这里插入图片描述

加减法运算符和“<<”重载为非成员函数形式复数类的UML图形表示

  1. #include<iostream>
  2. using namespace std;
  3. class Complex{
  4. public:
  5. Complex(double r = 0.0, double i = 0.0):real(r), imag(i){ }
  6. friend Complex operator+(const Complex &c1, const Complex &c2);
  7. friend Complex operator-(const Complex &c1, const Complex &c2);
  8. friend ostream & operator << (ostream &out, const Complex &c);
  9. private:
  10. double real;
  11. double imag;
  12. };
  13. Complex operator+(const Complex &c1, const Complex &c2){
  14. return Complex(c1.real + c2.real, c1.imag + c2.imag);
  15. }
  16. Complex operator-(const Complex &c1, const Complex &c2){
  17. return Complex(c1.real - c2.real, c1.imag - c2.imag);
  18. }
  19. ostream & operator<<(ostream &out, const Complex &c){
  20. out << "(" << c.real << "," << c.imag << ")";
  21. return out;
  22. }
  23. int main(){
  24. Complex c1(5, 4), c2(2, 10), c3;
  25. cout<<"c1 = "<< c1 << endl;
  26. cout<<"c2 = "<< c2 << endl;
  27. c3 = c1 - c2;
  28. cout<<"c3 = c1 - c2 = "<< c3 << endl;
  29. c3 = c1 + c2;
  30. cout<<"c3 = c1 + c2 = " << c3 << endl;
  31. return 0;
  32. }

运行结果:

  1. c1 = (5,4)
  2. c2 = (2,10)
  3. c3 = c1 - c2 = (3,-6)
  4. c3 = c1 + c2 = (7,14)

将运算符重载为类的非成员函数,就必须把操作数通过形参的方式传递给运算符重载函数。“<<”操作符的左操作数为ostream类型的引用,ostream是cout类型的一个基类,右操作数是Complex类型的引用,这样在执行cout<<c1时,就会调用operator<<(cout, c1)。该函数把通过第一个参数传入的ostream对象以引用形式返回,这是为了支持形如“cout << c1 <<c2”的连续输出,因为第二个”<<“运算符的左操作数是第一个”<<”运算符的返回结果。和上例相比,主函数中的“+”,“-”的用法没有改动,而对Complex对象的输出更加直观了,程序运行的结果完全相同。

《C++语言程序设计(第4版)》书上所有章节出现的示例源代码随着学习的深入会陆续上传至github,代码为个人手动输入并通过编译,有的示例代码可能没有注释:https://github.com/fyw4/C-plus-plus-learning-example

以上。

参考文档:
郑莉 董渊 何江舟.《C++语言程序设计(第4版)》[M].北京:清华大学出版社。

发表评论

表情:
评论列表 (有 0 条评论,307人围观)

还没有评论,来说两句吧...

相关阅读

    相关 c++运算符重载

    C++不允许把下标运算符函数作为外部函数来定义,它只能是非静态的成员函数。 注意操作符重载作为友元函数和成员函数的区别 // person.cpp : 定

    相关 c++运算符重载

    一、前言 重载运算符的目的是为了让c++代码变的更直观、易读,主要在类中定义,让我们的类对象有着和普通变量一样的操作,例如:“<<”输出运算符,我们也希望类对象可以使用它,

    相关 c++】运算符重载

    您可以重定义或重载大部分 C++ 内置的运算符。这样,您就能使用自定义类型的运算符。 重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运

    相关 c++运算符重载

    什么是运算符重载? 运算符重载是通过创建运算符函数实现的,运算符函数定义了重载的运算符将要进行的操作。运算符函数的定义与其他函数的定义类似,惟一的区别是运算符函数的

    相关 C++】 运算符重载

    1、一元运算符 运算符重载是`C++`的一大特色,以函数重载的形式出现,函数名为关键字operator后跟着一个运算符,这些函数可以作为类的成员函数实现,也可以作为类的友

    相关 C++学习_运算符重载

    运算符重载 1.概念:赋予已经存在的,允许重载的运算符新的含义。 2.不允许重载的运算符: ‘.’ 成员选择符 ‘::’域解析操作符 ‘?:’条件操作符