当前位置:   article > 正文

C/C++ - 类的友元与运算符重载

C/C++ - 类的友元与运算符重载

目录

类的特性

友元

友元函数

友元类

友元特性

运算符重载

运算符重载核心

运算符重载语法

成员函数方式重载

全局函数方式重载

两种方式不同特性

运算符重载符号

关系运算符

逻辑运算符

赋值运算

自增自减运算符

下标访问运算符

输入输出运算符

类的特性

  • 友元

    • 友元函数

      • 在C++中,友元函数是一个非成员函数,但它可以访问类的私有成员和保护成员。
      • 通过将函数声明为类的友元,我们可以授权函数访问类的私有成员,即使该函数不是类的成员函数。
      • 在类的定义中,可以使用friend​​​​关键字来声明友元函数。在类中声明的友元函数可以访问类的私有和保护成员。
      • 友元函数在类的定义之外进行定义,但在定义时需要使用类名和作用域解析运算符来指定该函数是类的友元函数。
      1. #include <iostream>
      2. class Person
      3. {
      4. public:
      5. //友元函数
      6. friend void FriendAsk(Person& ref);
      7. public:
      8. Person(int age, int money) : m_Aage(age), m_Money(money){}
      9. int GetInfo()
      10. {
      11. return this->m_Money;
      12. }
      13. public:
      14. int m_Aage;
      15. private:
      16. int m_Money;
      17. };
      18. void FriendAsk(Person& ref)
      19. {
      20. std::cout << ref.m_Money << std::endl;
      21. }
      22. int main()
      23. {
      24. Person p1(18, 100);
      25. std::cout << p1.GetInfo() << std::endl;
      26. FriendAsk(p1);
      27. return 0;
      28. }
    • 友元类

      • 友元类是指一个类可以访问另一个类的私有成员和保护成员。要将一个类声明为另一个类的友元类,可以在类的定义中使用friend​​​关键字。
      1. #include <iostream>
      2. class MyClass
      3. {
      4. private:
      5. int value;
      6. public:
      7. MyClass(int v) : value(v) {}
      8. friend class FriendClass;
      9. };
      10. class FriendClass {
      11. public:
      12. void displayValue(const MyClass& obj) {
      13. std::cout << "The value is: " << obj.value << std::endl;
      14. }
      15. };
      16. int main() {
      17. MyClass obj(42);
      18. FriendClass fc;
      19. fc.displayValue(obj); // 使用友元类的成员函数访问私有成员
      20. return 0;
      21. }
    • 友元特性

      • 友元函数的访问权限:

        • 友元函数具有对类的所有成员的访问权限,包括私有成员和保护成员。
        • 友元函数不是类的成员函数,因此它们不能通过类对象的成员访问运算符(.​​​)或箭头运算符(->​​​)来访问类的成员。
      • 友元函数的声明和定义:

        • 友元函数应该在类的声明中进行声明,以便在类中使用。
        • 友元函数的定义可以在类的声明之外进行,但是需要使用类名和作用域解析运算符来指定该函数是类的友元函数。
      • 友元函数和封装:

        • 友元函数可以突破类的封装性,因为它们可以直接访问类的私有成员。这可能会导致破坏类的封装性原则,因此在使用友元函数时应谨慎考虑。
      • 友元函数的应用:

        • 友元函数常用于重载运算符,使其能够操作类的私有成员。
        • 友元函数还可以在类之间共享信息或提供类之间的特定功能

  • 运算符重载

    • 运算符重载符号
      • 算术运算符:+、-、*、/、%​​​等

        1. #include <iostream>
        2. class Calc
        3. {
        4. public:
        5. //友元函数
        6. friend Calc operator-(const Calc& ref1, const Calc& ref2);
        7. //构造函数
        8. Calc(int nNum):n_Num(nNum){}
        9. Calc operator+(const Calc& ref)
        10. {
        11. return Calc(this->n_Num + ref.n_Num);
        12. }
        13. private:
        14. int n_Num;
        15. };
        16. Calc operator-(const Calc& ref1, const Calc& ref2)
        17. {
        18. return Calc(ref1.n_Num - ref2.n_Num);
        19. }
        20. int main()
        21. {
        22. Calc c1(10);
        23. Calc c2(5);
        24. Calc c3 = c1 + c2;
        25. Calc c4 = c1 - c2;
        26. return 0;
        27. }
      • 关系运算符
        • ==、!=、>、<、>=、<=​​​等

          1. #include <iostream>
          2. class Calc
          3. {
          4. public:
          5. //友元函数
          6. friend Calc operator-(const Calc& ref1, const Calc& ref2);
          7. //构造函数
          8. Calc(int nNum):n_Num(nNum){}
          9. //算数运算
          10. Calc operator+(const Calc& ref)
          11. {
          12. return Calc(this->n_Num + ref.n_Num);
          13. }
          14. Calc operator-(const Calc& ref)
          15. {
          16. return Calc(this->n_Num - ref.n_Num);
          17. }
          18. Calc operator*(const Calc& ref)
          19. {
          20. return Calc(this->n_Num * ref.n_Num);
          21. }
          22. Calc operator/(const Calc& ref)
          23. {
          24. return Calc(this->n_Num / ref.n_Num);
          25. }
          26. Calc operator%(const Calc& ref)
          27. {
          28. return Calc(this->n_Num % ref.n_Num);
          29. }
          30. //比较运算
          31. bool operator==(const Calc& ref)
          32. {
          33. return this->n_Num == ref.n_Num;
          34. }
          35. bool operator!=(const Calc& ref)
          36. {
          37. return this->n_Num != ref.n_Num;
          38. }
          39. bool operator>(const Calc& ref)
          40. {
          41. return this->n_Num > ref.n_Num;
          42. }
          43. bool operator<(const Calc& ref)
          44. {
          45. return this->n_Num < ref.n_Num;
          46. }
          47. bool operator>=(const Calc& ref)
          48. {
          49. return this->n_Num >= ref.n_Num;
          50. }
          51. bool operator<=(const Calc& ref)
          52. {
          53. return this->n_Num <= ref.n_Num;
          54. }
          55. private:
          56. int n_Num;
          57. };
          58. int main()
          59. {
          60. Calc c1(10);
          61. Calc c2(5);
          62. //算数运算
          63. Calc c3 = c1 + c2;
          64. //笔记运算
          65. bool bret = c1 > c2;
          66. return 0;
          67. }
      • 逻辑运算符
        • !、&&、||​​​等

          1. #include <iostream>
          2. class Calc
          3. {
          4. public:
          5. //构造函数
          6. Calc(int nNum) :n_Num(nNum) {}
          7. bool operator!()
          8. {
          9. return !this->n_Num;
          10. }
          11. bool operator||(const Calc& ref)
          12. {
          13. return this->n_Num || ref.n_Num;
          14. }
          15. bool operator&&(const Calc& ref)
          16. {
          17. return this->n_Num && ref.n_Num;
          18. }
          19. private:
          20. int n_Num;
          21. };
          22. int main()
          23. {
          24. Calc c1(10);
          25. Calc c2(5);
          26. // ||
          27. // &&
          28. // !
          29. std::cout << !c1 << std::endl;
          30. std::cout << (c1 || c2) << std::endl;
          31. std::cout << (c1 && c2) << std::endl;
          32. return 0;
          33. }
      • 赋值运算
        • =、+=、-=、*=、/=​​​等

          1. #include <iostream>
          2. class Number
          3. {
          4. private:
          5. int value;
          6. public:
          7. Number(int val) : value(val) {}
          8. // 赋值运算符重载:=
          9. Number& operator=(const Number& other)
          10. {
          11. if (this == &other)
          12. {
          13. return *this;
          14. }
          15. value = other.value;
          16. return *this;
          17. }
          18. // 复合赋值运算符重载:+=
          19. Number& operator+=(const Number& other)
          20. {
          21. value += other.value;
          22. return *this;
          23. }
          24. // 复合赋值运算符重载:-=
          25. Number& operator-=(const Number& other)
          26. {
          27. value -= other.value;
          28. return *this;
          29. }
          30. // 复合赋值运算符重载:*=
          31. Number& operator*=(const Number& other)
          32. {
          33. value *= other.value;
          34. return *this;
          35. }
          36. // 复合赋值运算符重载:/=
          37. Number& operator/=(const Number& other)
          38. {
          39. if (other.value == 0)
          40. {
          41. throw std::runtime_error("Divide by zero error");
          42. }
          43. value /= other.value;
          44. return *this;
          45. }
          46. void display() const
          47. {
          48. std::cout << "Value: " << value << std::endl;
          49. }
          50. };
          51. int main() {
          52. Number num1(10);
          53. Number num2(0);
          54. try
          55. {
          56. num1 += num2;
          57. num1.display();
          58. num1 -= num2;
          59. num1.display();
          60. num1 *= num2;
          61. num1.display();
          62. num1 /= num2;
          63. num1.display();
          64. }
          65. catch (const std::exception& e)
          66. {
          67. std::cout << e.what() << std::endl;
          68. }
          69. return 0;
          70. }
      • 自增自减运算符
        • ++、--​​​等

          1. #include <iostream>
          2. class Counter
          3. {
          4. private:
          5. int count;
          6. public:
          7. Counter(int c = 0) : count(c) {}
          8. // 前置自增运算符重载:++
          9. Counter& operator++()
          10. {
          11. count++;
          12. return *this;
          13. }
          14. // 后置自增运算符重载:++
          15. Counter operator++(int)
          16. {
          17. Counter temp = *this;
          18. ++(*this);
          19. return temp;
          20. }
          21. // 前置自减运算符重载:--
          22. Counter& operator--()
          23. {
          24. count--;
          25. return *this;
          26. }
          27. // 后置自减运算符重载:--
          28. Counter operator--(int)
          29. {
          30. Counter temp = *this;
          31. --(*this);
          32. return temp;
          33. }
          34. void display() const
          35. {
          36. std::cout << "Count: " << count << std::endl;
          37. }
          38. };
          39. int main()
          40. {
          41. Counter c(5);
          42. ++c;
          43. c.display();
          44. c--;
          45. c.display();
          46. return 0;
          47. }
      • 下标访问运算符
        • []​​​

          1. #include <iostream>
          2. class StudentInfo
          3. {
          4. public:
          5. StudentInfo(int nCount): m_Data(new int[nCount]), m_Count(nCount){}
          6. int& operator[](int nIndex)
          7. {
          8. return m_Data[nIndex];
          9. }
          10. void display() const
          11. {
          12. for (size_t i = 0; i < m_Count; i++)
          13. {
          14. std::cout << "m_Data[" << i <<"] -> " << m_Data[i] << std::endl;
          15. }
          16. }
          17. private:
          18. int m_Count;
          19. int* m_Data;
          20. };
          21. int main()
          22. {
          23. StudentInfo Info(5);
          24. Info[2] = 3;
          25. Info.display();
          26. return 0;
          27. }
      • 输入输出运算符
        • 重载输入运算符

          1. istream& operator>>(istream& input, YourType& object)
          2. {
          3. // 从输入流中读取数据,存储到 object
          4. // 对输入进行验证和处理
          5. // 返回输入流对象
          6. return input;
          7. }
        • 重载输出运算符

          1. ostream& operator<<(ostream& output, const YourType& object)
          2. {
          3. //object 的数据输出到输出流中
          4. // 格式化输出
          5. // 返回输出流对象
          6. return output;
          7. }
        • 示例代码

          1. #include <iostream>
          2. class Student
          3. {
          4. public:
          5. friend std::ostream& operator<<(std::ostream& output, const Student& object);
          6. friend std::istream& operator>>(std::istream& intput, Student& object);
          7. private:
          8. int m_Age;
          9. int m_Id;
          10. };
          11. std::ostream& operator<<(std::ostream& output, const Student& object)
          12. {
          13. // 将 object 的数据输出到输出流中
          14. output << object.m_Age << "|" << object.m_Id;
          15. // 返回输出流对象
          16. return output;
          17. }
          18. std::istream& operator>>(std::istream& intput, Student& object)
          19. {
          20. // 将 object 的数据输出到输出流中
          21. intput >> object.m_Age >> object.m_Id;
          22. // 返回输出流对象
          23. return intput;
          24. }
          25. int main()
          26. {
          27. Student s1;
          28. std::cin >> s1;
          29. std::cout << s1 << std::endl;
          30. return 0;
          31. }
    • 两种方式不同特性

      • 访问权限:

        • 成员函数:运算符重载作为类的成员函数,可以直接访问类的私有成员变量和其他成员函数。这提供了更方便的访问权限,避免了使用友元函数时需要将其声明为类的友元的复杂性。
        • 友元函数:运算符重载作为友元函数,可以在类的外部定义,并被授权访问类的私有成员。这允许了更大的灵活性,可以重载运算符以适应不同类型的操作数。
      • 左操作数和右操作数:

        • 成员函数:运算符重载作为成员函数时,左操作数是调用对象,右操作数是函数的参数。这使得运算符重载的使用看起来更像是对象的成员方法,例如 object1 + object2​​。
        • 友元函数:运算符重载作为友元函数时,左操作数是第一个参数,右操作数是第二个参数。这使得运算符重载的使用更加对称,例如 value1 + value2​​。
      • 参数传递方式:

        • 成员函数:运算符重载作为成员函数时,左操作数(调用对象)被隐式传递给函数,而右操作数作为显式参数传递。这意味着左操作数可以直接访问成员变量,而右操作数需要作为参数传递给函数。
        • 友元函数:运算符重载作为友元函数时,左操作数和右操作数都作为显式参数传递给函数。这意味着在友元函数中无法直接访问类的私有成员变量,需要使用访问器函数或公有接口来访问。
      • 对称性:

        • 成员函数:运算符重载作为成员函数时,左右操作数的顺序是固定的,无法改变。例如,对于 obj1 + obj2​​,左操作数将始终是 obj1​​。
        • 友元函数:运算符重载作为友元函数时,可以在重载时指定参数的顺序,从而实现对称性。例如,可以定义 operator+​​ 和 operator+=​​ 来支持 obj1 + obj2​​ 和 obj2 + obj1​​。
    • 全局函数方式重载

      • 全局函数方式的运算符重载是将运算符重载函数定义为全局函数,而不是类的成员函数。

      • 全局函数方式的运算符重载函数可以在函数定义中访问类的私有成员,通过将类声明为友元来实现。

        1. #include <iostream>
        2. class Complex {
        3. private:
        4. double real;
        5. double imag;
        6. public:
        7. Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}
        8. // 声明全局函数为友元
        9. friend Complex operator+(const Complex& c1, const Complex& c2);
        10. void display() const {
        11. std::cout << real << " + " << imag << "i" << std::endl;
        12. }
        13. };
        14. // 全局函数方式的运算符重载
        15. Complex operator+(const Complex& c1, const Complex& c2) {
        16. return Complex(c1.real + c2.real, c1.imag + c2.imag);
        17. }
        18. int main() {
        19. Complex c1(2.0, 3.0);
        20. Complex c2(4.0, 5.0);
        21. Complex sum = c1 + c2;
        22. sum.display(); // 输出:6 + 8i
        23. return 0;
        24. }
    • 成员函数方式重载

      • 成员函数方式的运算符重载是将运算符重载函数定义为类的成员函数。

      • 运算符重载函数作为成员函数时,隐含地访问了类的成员变量和成员函数。

        1. #include <iostream>
        2. class Complex {
        3. private:
        4. double real;
        5. double imag;
        6. public:
        7. Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}
        8. // 运算符重载函数作为类的成员函数
        9. Complex operator+(const Complex& other) const {
        10. return Complex(real + other.real, imag + other.imag);
        11. }
        12. void display() const {
        13. std::cout << real << " + " << imag << "i" << std::endl;
        14. }
        15. };
        16. int main() {
        17. Complex c1(2.0, 3.0);
        18. Complex c2(4.0, 5.0);
        19. Complex sum = c1 + c2;
        20. sum.display(); // 输出:6 + 8i
        21. return 0;
        22. }
    • 运算符重载语法

      1. 返回类型 operator 运算符(参数列表)
      2. {
      3. // 实现运算符的操作
      4. }
    • 运算符重载核心

      • 运算符重载是指为类类型的对象定义自定义行为的过程,使得对象可以使用像基本数据类型一样的运算符进行操作。
      • 通过运算符重载,我们可以使用自定义类型的对象进行像内置类型一样的操作。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/50920
推荐阅读
相关标签
  

闽ICP备14008679号