• 黑马程序员C++类和对象【5】 —— 运算符重载(蓝桥杯必备知识)万字超详解


    目录

    🤡加号运算符重载

    🤡左移运算符重载

    🤡递增运算符重载

    🤡递减运算符重载

    🤡赋值运算符重载

    🤡关系运算符重载

    🤡函数调用运算符重载(仿函数)


    🤡加号运算符重载

    如果现在我们希望对对象的属性进行求和,我们可能会想到用+直接进行相加,那么就让我们先来试试吧,下面是我实例化两个对象的两个属性相加,结果编译器会报错,说我的操作数和运算符不匹配,那么这时候就需要自己手动重载运算符了。

    作用:实现两个自定义数据类型相加的运算

    使用运算符重载(注意:重载函数名必须是编译器提供的operator+)


    成员函数重载

    1. //运算符重载
    2. class Person {
    3. public:
    4. //1、成员函数重载+号
    5. Person operator+(Person& p)
    6. {
    7. Person temp;
    8. //this指针指向的是调用该成员函数的对象(p1),这里面的p是指传进来的p2的引用
    9. temp.m_A = this->m_A + p.m_A;
    10. temp.m_B = this->m_B + p.m_B;
    11. return temp;
    12. }
    13. int m_A;
    14. int m_B;
    15. };
    16. void test01()
    17. {
    18. Person p1;
    19. p1.m_A = 10;
    20. p1.m_B = 10;
    21. Person p2;
    22. p2.m_A = 10;
    23. p2.m_B = 10;
    24. Person p3 = p1 + p2; //等价于 p1.operator+(p2)
    25. cout << p3.m_A << " " << p3.m_B << endl;
    26. }
    27. int main() {
    28. test01(); //调用
    29. system("pause");
    30. return 0;
    31. }

    全局函数重载

    1. //运算符重载
    2. class Person {
    3. public:
    4. //1、成员函数重载+号
    5. //Person operator+(Person& p)
    6. //{
    7. // Person temp;
    8. // temp.m_A = this->m_A + p.m_A;
    9. // temp.m_B = this->m_B + p.m_B;
    10. // return temp;
    11. //}
    12. int m_A;
    13. int m_B;
    14. };
    15. //2、全局函数重载 + 号
    16. Person operator+(Person &p1,Person &p2)
    17. {
    18. Person temp;
    19. temp.m_A = p1.m_A + p2.m_B;
    20. temp.m_B = p1.m_B + p2.m_B;
    21. return temp;
    22. }
    23. void test01()
    24. {
    25. Person p1;
    26. p1.m_A = 10;
    27. p1.m_B = 10;
    28. Person p2;
    29. p2.m_A = 10;
    30. p2.m_B = 10;
    31. //Person p3 = p1 + p2; //成员函数重载本质调用等价于 p1.operator+(p2)
    32. Person p3 = p1 + p2; //全局函数重载本质调用等价于 operator+(p1,p2)
    33. cout << p3.m_A << " " << p3.m_B << endl;
    34. }
    35. int main() {
    36. test01();
    37. system("pause");
    38. return 0;
    39. }

    运算符重载也可以发生函数重载

    1. class Person {
    2. public:
    3. Person() {};
    4. Person(int a, int b)
    5. {
    6. this->m_A = a;
    7. this->m_B = b;
    8. }
    9. //成员函数实现 + 号运算符重载
    10. Person operator+(const Person& p) {
    11. Person temp;
    12. temp.m_A = this->m_A + p.m_A;
    13. temp.m_B = this->m_B + p.m_B;
    14. return temp;
    15. }
    16. public:
    17. int m_A;
    18. int m_B;
    19. };
    20. //全局函数实现 + 号运算符重载
    21. //Person operator+(const Person& p1, const Person& p2) {
    22. // Person temp(0, 0);
    23. // temp.m_A = p1.m_A + p2.m_A;
    24. // temp.m_B = p1.m_B + p2.m_B;
    25. // return temp;
    26. //}
    27. //运算符重载 可以发生函数重载
    28. Person operator+(const Person& p2, int val)
    29. {
    30. Person temp;
    31. temp.m_A = p2.m_A + val;
    32. temp.m_B = p2.m_B + val;
    33. return temp;
    34. }
    35. void test() {
    36. Person p1(10, 10);
    37. Person p2(20, 20);
    38. //成员函数方式
    39. Person p3 = p2 + p1; //相当于 p2.operaor+(p1)
    40. cout << "mA:" << p3.m_A << " mB:" << p3.m_B << endl;
    41. Person p4 = p3 + 10; //相当于 operator+(p3,10)
    42. cout << "mA:" << p4.m_A << " mB:" << p4.m_B << endl;
    43. }
    44. int main() {
    45. test();
    46. system("pause");
    47. return 0;
    48. }

     总结1:对于内置的数据类型的表达式的的运算符是不可能改变的;

    总结2:不要滥用运算符重载


    🤡左移运算符重载

    比如这个例子,显然下面这个不能输出的。

    左移运算符重载的作用:可以输出自定义数据类型

    可以转到定义查看cout是ostream类型的 

    1. //左移运算符重载
    2. class Person {
    3. friend ostream& operator<<(ostream& out, Person& p);
    4. public:
    5. Person(int a, int b)
    6. {
    7. this->m_A = a;
    8. this->m_B = b;
    9. }
    10. //成员函数 实现不了( p << cout 不是我们想要的效果 )
    11. //void operator<<(Person& p){
    12. //}
    13. private:
    14. int m_A;
    15. int m_B;
    16. };
    17. //全局函数实现左移重载
    18. //ostream对象只能有一个
    19. ostream& operator<<(ostream& out, Person& p) {
    20. //内部就是一个简单的对象属性的输出
    21. out << "a:" << p.m_A << " b:" << p.m_B << endl;;
    22. return out;
    23. }
    24. void test() {
    25. Person p1(10, 20);
    26. //如果返回的是void型,后面就不能追加输出了
    27. cout << p1 << "hello world" << endl; //链式编程
    28. }
    29. int main() {
    30. test();
    31. system("pause");
    32. return 0;
    33. }

     总结:重载左移运算符配合友元可以实现输出自定义数据类型


    🤡递增运算符重载

    作用: 通过重载递增运算符,实现自己的整型数据

    1. class MyInteger {
    2. friend ostream& operator<<(ostream& out, MyInteger myint);
    3. public:
    4. MyInteger() {
    5. m_Num = 0;
    6. }
    7. //前置++
    8. MyInteger& operator++() {
    9. //先++
    10. m_Num++;
    11. //再返回
    12. return *this;
    13. }
    14. //后置++
    15. MyInteger operator++(int) {
    16. //先返回
    17. MyInteger temp = *this; //记录当前本身的值,然后让本身的值加1,但是返回的是以前的值,达到先返回后++;
    18. m_Num++;
    19. return temp;
    20. }
    21. private:
    22. int m_Num;
    23. };
    24. ostream& operator<<(ostream& out, MyInteger myint) {
    25. out << myint.m_Num;
    26. return out;
    27. }
    28. //前置++ 先++ 再返回
    29. void test01() {
    30. MyInteger myInt;
    31. cout << ++myInt << endl;
    32. cout << myInt << endl;
    33. }
    34. //后置++ 先返回 再++
    35. void test02() {
    36. MyInteger myInt;
    37. cout << myInt++ << endl;
    38. cout << myInt << endl;
    39. }
    40. int main() {
    41. test01();
    42. //test02();
    43. system("pause");
    44. return 0;
    45. }

     总结: 前置递增返回引用,后置递增返回值


    🤡递减运算符重载

    1. class MyInteger {
    2. //友元函数,是函数可以调用类里面的私有属性
    3. friend ostream& operator<<(ostream& out, MyInteger myint);
    4. public:
    5. MyInteger() {
    6. //给m_Num赋初值。
    7. m_Num = 2;
    8. }
    9. //前置--
    10. MyInteger& operator--() {
    11. //先 --
    12. --m_Num;
    13. //再返回
    14. return *this;
    15. }
    16. //后置--
    17. MyInteger operator--(int) {
    18. //先返回
    19. MyInteger temp = *this; //记录当前本身的值,然后让本身的减1,但是返回的是以前的值,达到先返回后--;
    20. m_Num--;
    21. return temp;
    22. }
    23. private:
    24. int m_Num;
    25. };
    26. //全局函数重载左移运算符用于输出
    27. ostream& operator<<(ostream& out, MyInteger myint) {
    28. out << myint.m_Num;
    29. return out;
    30. }
    31. //前置-- 先-- 再返回
    32. void test01() {
    33. MyInteger myInt;
    34. cout <<--myInt << endl;
    35. cout << myInt << endl;
    36. }
    37. //后置-- 先返回 再--
    38. void test02() {
    39. MyInteger myInt;
    40. cout << myInt-- << endl;
    41. cout << myInt << endl;
    42. }
    43. int main() {
    44. test01();
    45. test02();
    46. system("pause");
    47. return 0;
    48. }

     递减和递增一样,前置返回引用,后置返回值。


    🤡赋值运算符重载

    C++编译器至少给一个类添加4个函数

    1. 默认构造函数(无参,函数体为空)

    2. 默认析构函数(无参,函数体为空)

    3. 默认拷贝构造函数,对属性进行值拷贝

    4. 赋值运算符 operator=, 对属性进行值拷贝

    1. class Person
    2. {
    3. public:
    4. Person(int age) {
    5. m_Age = new int(age);
    6. }
    7. ~Person() {
    8. if (m_Age != NULL) {
    9. delete m_Age;
    10. m_Age = NULL;
    11. }
    12. }
    13. int *m_Age;
    14. };
    15. void test01()
    16. {
    17. Person p1(18);
    18. cout << "p1的年龄为:" << *p1.m_Age << endl;
    19. Person p2(28);
    20. p2 = p1;
    21. cout << "p1的年龄为:" << *p2.m_Age << endl;
    22. }
    23. int main() {
    24. test01();
    25. system("pause");
    26. return 0;
    27. }

    写了个析构函数,释放了自己手动在堆上面申请的内存 ,结果出现崩溃了。。。

     这里实际上是浅拷贝的问题。

     p1和p2公用一块堆内存18,并没有再重新开辟和p1的属性值一样的空间给p2,他们都指向这一块内存。

     在函数执行完之后,p2被释放了,再判断p2空间是否为空,不为空释放一次,p1进行同样操作。

    堆区内存重复释放,重新崩溃!

    解决方案:利用深拷贝,解决浅拷贝带来的问题。

    1. //重载赋值运算符
    2. void operator=(Person& p)
    3. {
    4. //编译器是提供浅拷贝
    5. //m_Age = p.m_Age;
    6. //应该先判断编译器是否有属性在堆区,如果有先释放干净,然后再深拷贝
    7. if (m_Age != NULL) {
    8. delete m_Age;
    9. m_Age = NULL;
    10. }
    11. //深拷贝
    12. m_Age = new int(*p.m_Age);

     直接在类里面添加上面代码就可以实现了

    但是,你以为这就完了吗

    如果这时候要求连续赋值操作,p3 = p2 = p1;则会报错,为啥呢?

    仔细看,你前面返回的是void类型,这里需要返回对象本体Person&。

    重载部分代码:

    1. //重载赋值运算符
    2. Person& operator=(Person& p)
    3. {
    4. //编译器是提供浅拷贝
    5. //m_Age = p.m_Age;
    6. //应该先判断编译器是否有属性在堆区,如果有先释放干净,然后再深拷贝
    7. if (m_Age != NULL) {
    8. delete m_Age;
    9. m_Age = NULL;
    10. }
    11. //深拷贝
    12. m_Age = new int(*p.m_Age);
    13. //返回对象本身
    14. return *this;
    15. }

     总结:

            先在堆区开辟内存

    重载流程:

    1.         判断是否有内存,进行清空操作。
    2.         进行深拷贝
    3.         返回自身

    🤡关系运算符重载

    作用:重载关系运算符,可以让两个自定义类型对象进行对比操作

    1. class Person
    2. {
    3. public:
    4. Person(string name, int age)
    5. {
    6. this->m_Name = name;
    7. this->m_Age = age;
    8. };
    9. //==的重载
    10. bool operator==(Person & p)
    11. {
    12. if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
    13. {
    14. return true;
    15. }
    16. else
    17. {
    18. return false;
    19. }
    20. }
    21. //!=的重载
    22. bool operator!=(Person & p)
    23. {
    24. if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
    25. {
    26. return false;
    27. }
    28. else
    29. {
    30. return true;
    31. }
    32. }
    33. string m_Name;
    34. int m_Age;
    35. };
    36. void test01()
    37. {
    38. //int a = 0;
    39. //int b = 0;
    40. Person a("孙悟空", 18);
    41. Person b("孙悟空", 18);
    42. if (a == b)
    43. {
    44. cout << "a和b相等" << endl;
    45. }
    46. else
    47. {
    48. cout << "a和b不相等" << endl;
    49. }
    50. if (a != b)
    51. {
    52. cout << "a和b不相等" << endl;
    53. }
    54. else
    55. {
    56. cout << "a和b相等" << endl;
    57. }
    58. }
    59. int main() {
    60. test01();
    61. system("pause");
    62. return 0;
    63. }

    🤡函数调用运算符重载(仿函数)

    • 函数调用运算符 () 也可以重载

    • 由于重载后使用的方式非常像函数的调用,因此称为仿函数

    • 仿函数没有固定写法,非常灵活

    1. class MyPrint
    2. {
    3. public:
    4. void operator()(string text)
    5. {
    6. cout << text << endl;
    7. }
    8. };
    9. void test01()
    10. {
    11. //重载的()操作符 因为调用的时候和函数调用很像,所以也称为仿函数
    12. MyPrint myFunc;
    13. myFunc("hello world");
    14. }
    15. class MyAdd
    16. {
    17. public:
    18. int operator()(int v1, int v2)
    19. {
    20. return v1 + v2;
    21. }
    22. };
    23. void test02()
    24. {
    25. MyAdd add;
    26. int ret = add(10, 10);
    27. cout << "ret = " << ret << endl;
    28. //匿名函数对象调用
    29. cout << "MyAdd()(100,100) = " << MyAdd()(100, 100) << endl;
    30. }
    31. int main() {
    32. test01();
    33. test02();
    34. system("pause");
    35. return 0;
    36. }

    记住:仿函数非常灵活,没有固定的写法。

  • 相关阅读:
    PMP(Project Management Professional)证在哪个行业比较有用?
    嫌学校软件太烂,父母做了开源APP,却被官方报警
    职素丨专业的职素训练 让学员转变为职业人
    01 【Vue简介 初识Vue 模板语法和数据绑定】
    dhrystone和coremark测试比较
    sql server服务无法启动怎么办?如何正常启动?
    Learn-1
    前后端分离计算机毕设项目之基于SpringBoot的旅游网站的设计与实现《内含源码+文档+部署教程》
    C++语言的由来与发展历程
    在不同的系统下解决apache2和nginx伪静态问题(非宝塔页面)wordpress通用
  • 原文地址:https://blog.csdn.net/m0_62853489/article/details/127632455