【C++】运算符重载 ⑨ ( 等号 = 运算符重载 | 调用默认浅拷贝构造函数的情况分析 | 等号 = 运算符重载 与 拷贝构造函数 各自使用场景 | 等号 = 操作符重载步骤 )

灰太狼 2024-02-25 03:15 111阅读 0赞

文章目录

  • 一、等号 = 运算符重载
    • 1、调用默认浅拷贝构造函数的情况分析
    • 2、等号 = 运算符重载 与 拷贝构造函数 各自使用场景
    • 3、= 操作符重载步骤
  • 二、完整代码示例

博客总结 :

使用 成员函数 进行 等号运算符重载 函数原型如下 :

  1. Student& operator=(Student& s)
  • 使用 已存在的对象 A 对 另外一个已存在对象 B 赋值 , B = A ,
  • 左操作数 B 是 this 指针 ;
  • 参数 Student& s 是 右操作数 ;
  • 返回 Student& 的原因是 等号 = 操作符是 右结合 的 , C = B = A 的情况 , 需要返回类对象 , 并支持链式操作 ;

一、等号 = 运算符重载


1、调用默认浅拷贝构造函数的情况分析

C++ 编译器 为 类 提供的 默认的 拷贝操作 , 是对 成员变量 的简单拷贝 , 是 浅拷贝 ;

在 【C++】深拷贝和浅拷贝 ③ ( 浅拷贝内存分析 ) 博客中 , 对浅拷贝进行了分析 , 使用 类对象 为 另一个对象 初始化时 , 会自动调用 拷贝构造函数 ;

  1. // 调用有参构造函数 , 创建 Student 实例对象
  2. Student s(18, "Tom");
  3. // 声明 Student 对象 s2 , 并使用 s 为 s2 赋值
  4. // 该操作会调用 默认的拷贝构造函数
  5. // C++ 编译器提供的拷贝构造函数 只能进行浅拷贝
  6. Student s2 = s;

在 【C++】深拷贝和浅拷贝 ④ ( 深拷贝示例 ) 博客中实现了 深拷贝构造函数 , 本篇博客主要以该 深拷贝 案例 进行拓展分析 ;

实现了 深拷贝构造函数 后 , 再次使用一个对象为另一个对象赋值时 , 如 Student s2 = s; 代码 , 就会自动调用 深拷贝构造函数 ;

2、等号 = 运算符重载 与 拷贝构造函数 各自使用场景

等号 = 运算符重载 与 拷贝构造函数 各自使用场景 :

  • 拷贝构造函数 : 如果 使用对象 为一个 新对象 进行初始化 , 调用的是拷贝构造函数 ;
  • 等号 = 运算符重载 : 如果 使用对象 为一个已存在的对象 重新进行赋值 , 调用的是 等号运算符 的 重载运算符方法 ;

3、= 操作符重载步骤

使用 成员函数 实现 等号 = 运算符重载 :

  • 首先 , 写出函数名 , Student s2 = s; 等号 = 运算符重载 , 函数名规则为 “ operate “ 后面跟上要重载的运算符 , 函数名是 operate= ;

    operate=

  • 然后 , 根据操作数 写出函数参数 , 参数一般都是 对象的引用 ;

    • Student s2 = s; 左操作数是 Student 类对象 , 参数中是引用类型 ;
    • Student s2 = s; 右操作数也是 Student 类对象 , 参数中是引用类型 ;
    • 如果是成员函数 , 则将重载函数写在 左操作数 中 , 在 重载操作数的 成员函数中 this 指针就是 左操作数 ;

    operator=(Student& s)

  • 再后 , 根据业务完善返回值 , 返回值可以是 引用 / 指针 / 元素 ;

    • 等号操作符 = 的结合顺序是 从右向左 ;
    • 如果出现 s1 = s2 = s3 的表达式 , 先执行 s2 = s3 , 再执行 s1 = ( s2 = s3 ) 语句 ;
    • 因此 , s2 = s3 返回值必须是一个 相同类型的 对象 , 否则会报错 ;
    • Student s2 = s; 操作需要返回 Student 类型的对象 ;
    • 如果要支持链式调用 , 必须返回的是 引用类型 ;

    Student& operator=(Student& s)

  • 最后 , 实现函数体 , 编写具体的运算符操作业务逻辑 ;

    • ① 释放旧内存 ; free(this->m_name);
    • ② 申请新内存 ; this->m_name = (char*)malloc(len + 1);
    • ③ 拷贝数据 ; strcpy(this->m_name, s.m_name);

    // 重载 等号 = 运算符

    1. Student& operator=(Student& s)
    2. {
    3. cout << "调用 Student 重载 等号 = 运算符" << endl;
    4. // 释放旧内存, 销毁 name 指向的堆内存空间
    5. if (this->m_name != NULL)
    6. {
    7. free(this->m_name);
    8. m_name = NULL;
    9. }
    10. // 申请新的内存
    11. // 获取字符串长度
    12. int len = strlen(s.m_name);
    13. // 为 m_name 成员分配内存
    14. // 注意还要为字符串结尾的 '\0' 字符分配内存
    15. this->m_name = (char*)malloc(len + 1);
    16. // 拷贝字符串
    17. // C++ 中使用该函数需要
    18. // 添加 #define _CRT_SECURE_NO_WARNINGS 宏定义
    19. if (this->m_name != NULL)
    20. {
    21. strcpy(this->m_name, s.m_name);
    22. }
    23. // 为 m_age 成员设置初始值
    24. this->m_age = s.m_age;
    25. // 等号操作符 = 的结合顺序是 从右向左 ;
    26. // 如果出现 s1 = s2 = s3 的表达式 , 先执行 s2 = s3 , 再执行 s1 = ( s2 = s3 ) 语句 ;
    27. // 因此 , s2 = s3 返回值必须是一个 相同类型的 对象 , 否则会报错 ;
    28. // 如果要支持链式调用 , 必须返回的是 引用类型 ;
    29. return *this;
    30. }

二、完整代码示例


代码示例 :

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include "iostream"
  3. using namespace std;
  4. class Student
  5. {
  6. public:
  7. // 有参构造函数
  8. Student(int age, const char* name)
  9. {
  10. // 获取字符串长度
  11. int len = strlen(name);
  12. // 为 m_name 成员分配内存
  13. // 注意还要为字符串结尾的 '\0' 字符分配内存
  14. m_name = (char*)malloc(len + 1);
  15. // 拷贝字符串
  16. // C++ 中使用该函数需要
  17. // 添加 #define _CRT_SECURE_NO_WARNINGS 宏定义
  18. if (m_name != NULL)
  19. {
  20. strcpy(m_name, name);
  21. }
  22. // 为 m_age 成员设置初始值
  23. m_age = age;
  24. cout << "调用有参构造函数" << endl;
  25. }
  26. // 拷贝构造函数
  27. // 执行 Student s2 = s; 代码时调用该函数
  28. // 自己实现 深拷贝 操作
  29. Student(const Student& s)
  30. {
  31. // 获取字符串长度
  32. int len = strlen(s.m_name);
  33. // 为 m_name 成员分配内存
  34. // 注意还要为字符串结尾的 '\0' 字符分配内存
  35. m_name = (char*)malloc(len + 1);
  36. // 拷贝字符串
  37. // C++ 中使用该函数需要
  38. // 添加 #define _CRT_SECURE_NO_WARNINGS 宏定义
  39. if (m_name != NULL)
  40. {
  41. strcpy(m_name, s.m_name);
  42. }
  43. // 为 m_age 成员设置初始值
  44. m_age = s.m_age;
  45. cout << "调用拷贝构造函数" << endl;
  46. }
  47. ~Student()
  48. {
  49. // 销毁 name 指向的堆内存空间
  50. if (m_name != NULL)
  51. {
  52. free(m_name);
  53. m_name = NULL;
  54. }
  55. cout << "调用析构函数" << endl;
  56. }
  57. // 该类没有定义拷贝构造函数 , C++ 编译器会自动生成默认的拷贝构造函数
  58. public:
  59. // 打印类成员变量
  60. void toString()
  61. {
  62. cout << "m_age = " << m_age << " , m_name = " << m_name << endl;
  63. }
  64. // 重载 等号 = 运算符
  65. Student& operator=(Student& s)
  66. {
  67. cout << "调用 Student 重载 等号 = 运算符" << endl;
  68. // 释放旧内存, 销毁 name 指向的堆内存空间
  69. if (this->m_name != NULL)
  70. {
  71. free(this->m_name);
  72. m_name = NULL;
  73. }
  74. // 申请新的内存
  75. // 获取字符串长度
  76. int len = strlen(s.m_name);
  77. // 为 m_name 成员分配内存
  78. // 注意还要为字符串结尾的 '\0' 字符分配内存
  79. this->m_name = (char*)malloc(len + 1);
  80. // 拷贝字符串
  81. // C++ 中使用该函数需要
  82. // 添加 #define _CRT_SECURE_NO_WARNINGS 宏定义
  83. if (this->m_name != NULL)
  84. {
  85. strcpy(this->m_name, s.m_name);
  86. }
  87. // 为 m_age 成员设置初始值
  88. this->m_age = s.m_age;
  89. // 等号操作符 = 的结合顺序是 从右向左 ;
  90. // 如果出现 s1 = s2 = s3 的表达式 , 先执行 s2 = s3 , 再执行 s1 = ( s2 = s3 ) 语句 ;
  91. // 因此 , s2 = s3 返回值必须是一个 相同类型的 对象 , 否则会报错 ;
  92. // 如果要支持链式调用 , 必须返回的是 引用类型 ;
  93. return *this;
  94. }
  95. public:
  96. int m_age;
  97. char* m_name;
  98. };
  99. int main()
  100. {
  101. // 调用有参构造函数 , 创建 Student 实例对象
  102. Student s(18, "Tom");
  103. // 打印 Student 实例对象成员变量值
  104. s.toString();
  105. // 声明 Student 对象 s2 , 并使用 s 为 s2 赋值
  106. // 该操作会调用 默认的拷贝构造函数
  107. // C++ 编译器提供的拷贝构造函数 只能进行浅拷贝
  108. Student s2(12, "Jerry");
  109. s2.toString();
  110. // 修改 s 对象
  111. // 此时调用的不是拷贝构造函数
  112. // 而是重载的等号操作符
  113. s = s2;
  114. s.toString();
  115. s2.toString();
  116. // 执行时没有问题 , 两个对象都可以正常访问
  117. // 但是由于拷贝时 执行的是浅拷贝
  118. // 浅拷贝 字符串指针时 , 直接将指针进行拷贝 , 没有拷贝具体的值
  119. // s 和 s2 的 m_name 成员是同一个指针
  120. // 如果析构时 , 先析构 s2 , 将指针释放了
  121. // 之后再析构 s 时 发现 继续释放 被释放的指针 , 报错了
  122. // 控制台暂停 , 按任意键继续向后执行
  123. system("pause");
  124. return 0;
  125. }

执行结果 :

  1. 调用有参构造函数
  2. m_age = 18 , m_name = Tom
  3. 调用有参构造函数
  4. m_age = 12 , m_name = Jerry
  5. 调用 Student 重载 等号 = 运算符
  6. m_age = 12 , m_name = Jerry
  7. m_age = 12 , m_name = Jerry
  8. Press any key to continue . . .
  9. 调用析构函数
  10. 调用析构函数

在这里插入图片描述

发表评论

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

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

相关阅读