• C++之运算符重载


    目录

    1.运算符重载:1.可实现的对象

    ​编辑2.定义运算符重载函数的一般格式: 

    3.运算符的重载实际

    2.为什么要重载?

     3.前提:

    4.如何重载?

    5.默认

    6.指针作为数据成员

    7.字符串重载

    8.友元重载:重载输出<<运算符 

    9.运算符重载函数的总结

    10.重载运算符有以下几种限制


    1.运算符重载
    1.可实现的对象

    --让类类型的对象像基本数据类型一样去操作,例如,可以实现对象+,-,*,/,%,==,! = [] () << >>等


    2.定义运算符重载函数的一般格式: 

    返回类型 operator 运算符(参数列表) 比如:operator=(const A&str)

    返回值类型 类名::operator重载的运算符(参数表){……}
    operator是关键字,它与重载的运算符一起构成函数名。因函数名的特殊性,C++编译器可以将这
    类函数识别出来。

    3.运算符的重载实际

    是一种特殊的函数重载,必须定义一个函数,并告诉C++编译器,当遇到该重载的运算符时调用此函数。这个函数叫做运算符重载函数,通常为类的成员函数。

    2.为什么要重载?

    如何实现两个同类的对象相加?

    1. class A
    2. {
    3. public:
    4. A(int i=0):m_i(i){}
    5. private:
    6. int m_i;
    7. };
    8. void main()
    9. {
    10. A a(5), b(7);
    11. cout<
    12. }

     我们发现两个A类对象并不能直接相加。

    1. class A
    2. {
    3. public:
    4. A(int i=0):m_i(i){}
    5. private:
    6. int m_i;
    7. };
    8. class STR
    9. {
    10. public:
    11. STR(const char* str = "\0")
    12. {
    13. m_str = new char[strlen(str) + 1];
    14. strcpy_s(m_str, strlen(str) + 1, str);
    15. }
    16. private:
    17. char* m_str;
    18. };
    19. void main()
    20. {
    21. A a(5), b(7);
    22. //cout<
    23. int aa = 5, bb = 7;
    24. cout << aa + bb << endl;
    25. STR s("1234567");
    26. string str = "123456";
    27. string s1 = "123", s2 = "456";
    28. string s3 = s1 + s2;
    29. cout << str << endl;
    30. str[3] = 'k';
    31. cout << str << endl;
    32. cout << "s3= " << s3 << endl;
    33. //s[2] = 'k';
    34. }

    我们发现如果将我们写的类的内容,直接写为int这一大类的内容相加的时候是可以实现的,于是我们可以通过之前的接口,将A类的i直接获得,变为两个int类的相加

     

    1. class A
    2. {
    3. public:
    4. A(int i = 0,int j=0) :m_i(i),m_j(j)
    5. {
    6. }
    7. const int& GetI()const
    8. {
    9. return m_i;
    10. }
    11. const int& GetJ()const
    12. {
    13. return m_j;
    14. }
    15. A ADD(const A& b)
    16. {
    17. return A(m_i+b.m_i,m_j+b.m_j);
    18. }
    19. void Print()
    20. {
    21. cout << m_i << " " << m_j << endl;
    22. }
    23. private:
    24. int m_i;
    25. int m_j;
    26. };
    27. void main()
    28. {
    29. A a(5), b(7);
    30. //cout<
    31. //cout << a.m_i + b.m_i << endl;//error//本质上就是把对象的数据成员进行相加
    32. cout << a.GetI() + b.GetI() << endl;
    33. cout << a.GetJ() + b.GetJ() << endl;
    34. a.ADD(b).Print();
    35. }

    看起来不直观,可读性低

    并且当类的成员越来越多时,这样显得非常麻烦

     3.前提:

    1. void main()
    2. {
    3. int a = 10, b = 3;
    4. int c = 0;
    5. c = a + b;
    6. //a+b=c;//error
    7. ++a = c;
    8. //b++=13;//error
    9. cout << a << endl;
    10. (a = b) = c;
    11. }

    1.c = a + b;//从a的内存空间把a的值取出来,从b的内存空间把b的值取出来,把相加过的值放在c的空间

    2.a+b=c;//error//从c的内存空间把c的值取出来,a+b(的空间是临时空间,表达式结束,空间就释放了)没有确定的(永久)空间存放c
    3.++a = c;//(现在的a=11=执行过表达式后的a的值)将c放到a的存储单元
    4.//b++=13;//error//(b=3,b++=4,值不一样,b++没有确定空间)

    4.如何重载?

    1. class A
    2. {
    3. public:
    4. A(int i=0):m_i(i)//构造函数可作为类型转换
    5. {
    6. }
    7. void Print()
    8. {
    9. cout << m_i << endl;
    10. }
    11. A operator+(const A&b)//本质是实现对象的数据成员的加法
    12. {
    13. return m_i + b.m_i;
    14. }
    15. //A operator-(const A& a)//b-a b.operator-(a)本质是实现对象的数据成员的减法
    16. //{
    17. // return this->m_i - a.m_i;
    18. //}
    19. A operator-(const A& s)//b-a b.operator-(a)本质是实现对象的数据成员的减法
    20. {
    21. return this->m_i - s.m_i;
    22. }
    23. A& operator++()//++a a.++
    24. {
    25. ++m_i;//将a自己的数据成员进行修改
    26. return *this;//修改完后,返回自己本身
    27. }
    28. /*
    29. * b++,表达式的值是,b没加的值,执行完成后,b+1,所有在重载之后++,得体现两种值
    30. */
    31. A operator++(int)//int 没有实际性作用,只是为了区分前置++
    32. {
    33. int t = m_i;//保存自己
    34. m_i = m_i + 1;//+1
    35. return t;//返回没加以前保存的值
    36. //return m_i++//等价与上面三句话
    37. }
    38. private:
    39. int m_i;
    40. };
    41. int main()
    42. {
    43. A a(2), b(6);
    44. (a + b).Print();
    45. (b - a).Print();
    46. (a - b).Print();
    47. (++a).Print();
    48. (b++).Print();
    49. b.Print();
    50. }

        (a + b).Print();//a.+(b)//成员形式//(a,b)//友元
        (b - a).Print();//b.-(a);
        (a - b).Print();//a.-(b)
        (++a).Print();//3
        (b++).Print();//b加之前//6
        b.Print();//上式+1 7

    5.默认

    如果程序员没有提供赋值运算符,则类会提供一个默认的,默认的功能为:
             用右边的对象的数据成员依次给左边对象的数据成员赋值

    1. class A
    2. {
    3. public:
    4. A(int i = 0, int j = 0) :m_i(i), m_j(j)
    5. {
    6. }
    7. void Print()
    8. {
    9. cout << m_i << " " << m_j << endl;
    10. }
    11. private:
    12. int m_i;
    13. int m_j;
    14. };
    15. void main()
    16. {
    17. A a(2, 6);
    18. A b;
    19. a.Print();
    20. b.Print();
    21. b = a;
    22. b.Print();
    23. }

       

        a.Print();// 2 6
        b.Print();// 0 0
        b = a;// 2 6用a的值重新给b赋值,调用了默认的赋值运算符重载函数
        b.Print();

    6.指针作为数据成员

    1. class Person
    2. {
    3. public:
    4. Person(const char*name="\0")
    5. {
    6. m_name = new char[strlen(name) + 1];
    7. strcpy_s(m_name, strlen(name) + 1, name);
    8. }
    9. void Print()
    10. {
    11. cout << m_name << endl;
    12. }
    13. ~Person()
    14. {
    15. delete[]m_name;
    16. m_name = NULL;
    17. }
    18. Person& operator=(const Person& s)//析构+拷贝构造
    19. {
    20. if (this == &s)//判断自赋值
    21. return *this;
    22. delete[]m_name;
    23. m_name = new char[strlen(s.m_name) + 1];
    24. strcpy_s(m_name, strlen(s.m_name) + 1, s.m_name);
    25. return *this;
    26. }
    27. private:
    28. char* m_name;
    29. };
    30. void main()
    31. {
    32. Person p1("lisi");
    33. Person p2("11");
    34. p1.Print();
    35. p2.Print();
    36. p2 = p1;
    37. p2.Print();
    38. }

    总结:

    如果有指针作为数据成员,则赋值运算符重载也要写出来,让两个而不同的对象的指针分别指向内存单元,不过里面的内容保持相同
    * 赋值=能作为左值,所以引用返回

    if (this == &s) //判断自赋值
                return *this;
            //先将左边对象原本的内存空间释放掉
            delete[]m_name;
            //接着开辟和右边对象s相同大小的内存单元
            m_name = new char[strlen(s.m_name) + 1];
            strcpy_s(m_name, strlen(s.m_name) + 1, s.m_name);

    p2 = p1;  //用p1给p2重新赋值,如果调用默认的赋值重载,则让p2的指针重新指向了p1的指针,p1和p2的指针指向同一块空间,但是p2原本的空间还在

    7.字符串重载

    1. class STR
    2. {
    3. public:
    4. STR(const char* str = "\0")
    5. {
    6. m_str = new char[strlen(str) + 1];
    7. strcpy_s(m_str, strlen(str) + 1, str);
    8. }
    9. STR(const STR& s)
    10. {
    11. m_str = new char[strlen(s.m_str) + 1];
    12. strcpy_s(m_str, strlen(s.m_str) + 1, s.m_str);
    13. }
    14. ~STR()
    15. {
    16. cout << "~STR " << m_str << endl;
    17. if (m_str != NULL)
    18. {
    19. delete[]m_str;
    20. m_str = NULL;
    21. }
    22. }
    23. STR operator+(const STR& s)//a+b 其实就是将两个对象中的字符串合并
    24. {
    25. char* temp = new char[strlen(m_str) + strlen(s.m_str) + 1];
    26. strcpy_s(temp, strlen(m_str)+1, m_str);
    27. strcat_s(temp,strlen(m_str)+strlen(s.m_str)+1, s.m_str);
    28. STR ss(temp);
    29. delete[]temp;
    30. temp = NULL;
    31. return ss;
    32. }
    33. STR& operator=(const STR& s)
    34. {
    35. if (this == &s)
    36. return *this;
    37. delete[]m_str;
    38. m_str = new char[strlen(s.m_str) + 1];
    39. strcpy_s(m_str, strlen(s.m_str) + 1, s.m_str);
    40. return *this;
    41. }
    42. void Print()
    43. {
    44. cout << "m_str = "<
    45. }
    46. char& operator[](int index)
    47. {
    48. if (index >= 0 && index < strlen(m_str)) //"1234"
    49. return m_str[index];
    50. }
    51. int operator==(const STR& s)
    52. {
    53. return strcmp(m_str, s.m_str)==0;
    54. }
    55. private:
    56. char* m_str;
    57. };
    58. void main()
    59. {
    60. STR a("111"), b("222");
    61. cout << "a:" << endl;
    62. a.Print();
    63. cout << "b:" << endl;
    64. b.Print();
    65. STR c = a + b;//拷贝构造 111222
    66. cout << "c:" << endl;
    67. c.Print();//111222
    68. STR d;
    69. d = c;//赋值=运算符重载
    70. cout << "d:" << endl;
    71. d.Print();//111222
    72. c[5] = 'h';
    73. cout << "c[5]" << endl;
    74. cout << c[5] << endl;//11122h
    75. cout << "c:" << endl;
    76. c.Print();
    77. cout << "a==b" << endl;
    78. cout << (a == b) << endl;//0
    79. cout << "a!=b" << endl;
    80. cout << (a != b) << endl;//1
    81. }

    8.友元重载:重载输出<<运算符 

    cout--ostream类的对象
    cin--istream类的对象
    cout << a;
    一般情况下运算符可以解析成下面两种形式
    1.cout.<<(a)   ostream类中重载了<<,程序员不能修改
    2.<<(cout,a)   可以在程序员自己定义的类中将<<重载成友元
    cout< cout< (cout<

    friend ostream& operator<<(ostream &out,A &a)

    1. class Magic
    2. {
    3. double x;
    4. public:
    5. Magic(double d=0.00):x(fabs(d)){}
    6. Magic operator+(Magic c)
    7. {
    8. return Magic(sqrt(x * x + c.x * c.x));
    9. }
    10. friend ostream& operator << (ostream& os, Magic c);
    11. };
    12. ostream& operator << (ostream& os, Magic c)
    13. {
    14. return os << c.x;
    15. }
    16. void main()
    17. {
    18. Magic ma;
    19. cout << ma << ',' << Magic(-8) << "," << ma + Magic(-3) + Magic(-4);
    20. }

    9.运算符重载函数的总结

    1、运算符重载函数的函数名必须为关键字operator加一个合法的运算符。在调用该函数时,将右 操作数作为函数的实参。

    2、当用类的成员函数实现运算符的重载时,运算符重载函数的参数(当为双目运算符时)为一个 或(当为单目运算符时)没有。运算符的左操作数一定是对象,因为重载的运算符是该对象的成员函 数,而右操作数是该函数的参数。

    3、单目运算符“++”和“--”存在前置与后置问题。 前置“++”格式为: 返回类型 类名::operator++(){……} 而后置“++”格式为: 返回类型 类名::operator++(int){……} 后置“++”中的参数int仅用作区分,并无实际意义,可以给一个变量名,也可以不给变量名。

    4、C++中只有极少数的运算符不允许重载。

    还有 # , ## , // , / * */ 

    10.重载运算符有以下几种限制

    不可臆造新的运算符.

    不能改变运算符原有的优先级、结合性和语法结构,不能改变运算符操作数的个数.

    运算符重载不宜使用过多.

    重载运算符含义必须清楚,不能有二义性

  • 相关阅读:
    spring之AOP的概念及简单案例
    迭代器 Iterator
    Leetcode2-两数相加代码详解
    weak的底层原理
    sqllab第三关通关笔记
    APISIX、APISIX Dashboard搭建及插件使用
    后台管理系统SQL注入漏洞
    抛弃BeanUnits.copyProperties三部曲
    C# 6.0 添加和增强的功能【基础篇】
    牛客网《剑指offer》专栏刷题练习|锻炼递归思想|练习栈的使用
  • 原文地址:https://blog.csdn.net/m0_59052131/article/details/127622009