• C++ 运算符重载详解


    本篇内容来源于对c++课堂上学习内容的记录

    通过定义函数实现任意数据类型的运算

    假设我们定义了一个复数类,想要实现两个复数的相加肯定不能直接使用“+”运算符,我们可以通过自定义一个函数来实现这个功能:

    1. #include
    2. using namespace std;
    3. class Complex //定义Complex类
    4. {public:
    5. Complex( ){real=0;imag=0;} //定义构造函数
    6. Complex(double r,double i){real=r;imag=i;} //构造函数重载
    7. Complex complex_add(Complex &c2); //声明复数相加函数
    8. void display( ); //声明输出函数
    9. private:
    10. double real; //实部
    11. double imag; //虚部
    12. };
    13. Complex Complex∷complex_add(Complex &c2)
    14. { Complex c;
    15. c.real=real+c2.real;
    16. c.imag=imag+c2.imag;
    17. return c;
    18. }
    19. void Complex∷display( ) //定义输出函数
    20. {cout<<″(″<
    21. int main( )
    22. {
    23. Complex c1(3,4),c2(5,-10),c3; //定义3个复数对象
    24. c3=c1.complex_add(c2); //调用复数相加函数
    25. cout<<″c1=″; c1.display( ); //输出c1的值
    26. cout<<″c2=″; c2.display( ); //输出c2的值
    27. cout<<″c1+c2=″; c3.display( ); //输出c3的值
    28. return 0;
    29. }

    使用运算符重载实现上述功能

    运算符重载是指在编程语言中,为用户自定义的数据类型定义新的行为,使其可以像内置数据类型一样使用运算符。通过运算符重载,程序员可以对用户自定义类型进行特定的操作,而不仅仅局限于语言提供的默认行为。

    在大多数编程语言中,运算符重载通常涉及到重定义类或对象的特定方法,以实现对应运算符的功能。具体步骤包括:

    选择运算符: 选择要重载的运算符。这可以是算术运算符(如+-)、关系运算符(如==!=)、位运算符、赋值运算符等。

    定义重载函数: 在类或对象中定义与选择的运算符相关的特定方法。这些方法通常被称为运算符重载函数。在许多语言中,这些函数具有特殊的名称,例如C++中的 operator+operator==

    对于上述的例子,要想使用运算符重载,仅仅需要把复数类中的函数名称由complex_add变成operator+即可,然后就可以直接使用运算符“+”对两个复数进行相加:

    重载运算符的一些规则

    1.C++不允许用户自己定义新的运算符,只能对已有的运算符进行重载

    2.C++有几个运算符是不能重载的,分别是:

    .(成员访问运算符)

    *(成员指针访问运算符)

    ::(域运算符)

    sizeof(长度运算符)

    ?:(三目运算符)

    3.重载不能改变操作数个数

    4.重载不能改变优先级

    5.重载不能改变结合性

    6.重载的运算符必须和用户定义类型一起使用,参数不能全是C++标准类型

    7.有些运算符不用重载就可以对用户定义类型的对象使用,比如“=”和“&”

    友元重载运算符

    首先简单解释一下友元

    在面向对象编程中,友元(Friend)是一种机制,允许一个类或函数访问另一个类的私有成员。这就意味着,如果一个类或函数被声明为另一个类的友元,它就可以直接访问该类的私有成员,而不受访问权限的限制。

    友元的主要用途是在某些情况下,允许外部的类或函数访问另一个类的私有部分,以实现更灵活的设计或提高效率。一般而言,友元关系是单向的,即如果类 A 是类 B 的友元,不一定意味着类 B 是类 A 的友元

    友元在重载运算符中有什么用呢?

    考虑这么一个问题,我想让上述的复数和一个整数相加怎么办,其实很简单,只需要把operator+中的参数类型由Complex改成int就行了,这样,在下面调用的时候,就可以写:

    Complex c=a+2;

    但是,如果我们写成:

    Complex c=2+a;有没有问题呢?当然有问题,这样就是参数列表中的类型顺序不照应了

    因此,如果运算符左侧操作数属于C++标准类型或者是一个其它类的对象,则运算符重载函数不能作为成员函数,只能作为非成员函数,如果函数需要访问类的私有成员,则必须声明为友元函数

    举个例子:

    1. #include
    2. using namespace std;
    3. class Complex
    4. {public:
    5. Complex( ){real=0;imag=0;}
    6. Complex(double r,double i)
    7. {real=r;imag=i;}
    8. friend Complex operator +
    9. (Complex &c1,Complex &c2);
    10. //重载函数作为友元函数
    11. void display( );
    12. private:
    13. double real;
    14. double imag;
    15. };
    16. Complex operator + (Complex &c1,Complex &c2)
    17. //定义作为友元函数的重载函数
    18. {return Complex(c1.real+c2.real, c1.imag+c2.imag);}
    19. void Complex∷display( )
    20. {cout<<″(″<
    21. int main( )
    22. {Complex c1(3,4),c2(5,-10),c3;
    23. c3=c1+c2;
    24. cout<<″c1=″; c1.display( );
    25. cout<<″c2=″; c2.display( );
    26. cout<<″c1+c2 =″; c3.display( );}

    单目运算符的重载

    由于单目运算符本身就一个参数,那么如果运算符重载函数作为成员函数的话,就可以忽略参数

    首先看自增运算符++和自减运算符--的重载

    此时我们面临一个问题,这两个符号分为前置和后置两种情况,如果区分呢?

    C++约定: 在自增(自减)运算符重载函数中,增加一个int型形参,就是后置自增(自减)运算符函数

    重载后置自增运算符时,多了一个int型的参数,增加这个参数只是为了与前置自增运算符重载函数有所区别,此外没有任何作用

    举个例子:

    1. #include
    2. class Counter {
    3. private:
    4. int count;
    5. public:
    6. Counter() : count(0) {}
    7. // 重载前置自增运算符 (++var)
    8. Counter& operator++() {
    9. count++;
    10. return *this; // 返回递增后的对象引用
    11. }
    12. // 重载后置自增运算符 (var++)
    13. Counter operator++(int) {
    14. Counter temp(*this); // 创建一个副本用于保存递增前的值
    15. count++;
    16. return temp; // 返回递增前的对象副本
    17. }
    18. void display() const {
    19. std::cout << "Count: " << count << std::endl;
    20. }
    21. };
    22. int main() {
    23. Counter myCounter;
    24. // 使用前置自增运算符
    25. ++myCounter;
    26. myCounter.display();
    27. // 使用后置自增运算符
    28. Counter anotherCounter = myCounter++;
    29. myCounter.display();
    30. anotherCounter.display();
    31. return 0;
    32. }

    重载“<<”和">>"

    格式:

    重载"<<"

    1. #include
    2. class Point {
    3. private:
    4. int x, y;
    5. public:
    6. Point(int xCoord, int yCoord) : x(xCoord), y(yCoord) {}
    7. // 重载输出运算符
    8. friend std::ostream& operator<<(std::ostream& out, const Point& point);
    9. std::ostream& operator<<(std::ostream& out, const Point& point) {
    10. out << "Point(" << point.x << ", " << point.y << ")";
    11. return out;
    12. }
    13. int main() {
    14. Point myPoint(3, 4);
    15. // 使用重载的输出运算符
    16. std::cout << "My Point: " << myPoint << std::endl;
    17. return 0;
    18. }

    为什么在函数当中最后要返回out呢?

    可以满足链式编程,实现类似:cout<

    下面看对">>"的重载

    1. #include
    2. class Point {
    3. private:
    4. int x, y;
    5. public:
    6. Point() : x(0), y(0) {}
    7. // 重载输入运算符
    8. friend std::istream& operator>>(std::istream& in, Point& point);
    9. // 用于显示坐标的成员函数
    10. void display() const {
    11. std::cout << "Point(" << x << ", " << y << ")";
    12. }
    13. };
    14. std::istream& operator>>(std::istream& in, Point& point) {
    15. std::cout << "Enter x-coordinate: ";
    16. in >> point.x;
    17. std::cout << "Enter y-coordinate: ";
    18. in >> point.y;
    19. return in;
    20. }
    21. int main() {
    22. Point myPoint;
    23. // 使用重载的输入运算符
    24. std::cout << "Please enter coordinates for a point:\n";
    25. std::cin >> myPoint;
    26. // 使用成员函数显示坐标
    27. std::cout << "You entered: ";
    28. myPoint.display();
    29. std::cout << std::endl;
    30. return 0;
    31. }

    转换构造函数和类型转换函数

    转换构造函数(conversion constructor function) 的作用是将一个其他类型的数据转换成一个类的对象,本质是就是一种特殊的有参构造函数

    1. class Celsius {
    2. private:
    3. double temperature;
    4. public:
    5. // 转换构造函数,将double类型转换为Celsius对象
    6. Celsius(double temp) : temperature(temp) {}
    7. void display() {
    8. std::cout << "Temperature in Celsius: " << temperature << " C" << std::endl;
    9. }
    10. };
    11. int main() {
    12. // 使用转换构造函数,将double类型转换为Celsius对象
    13. Celsius celsiusObject = 25.5;
    14. celsiusObject.display();
    15. return 0;
    16. }

    类型转换函数:用转换构造函数可以将一个指定类型的数据转换为类的对象。但是不能反过来将一个类的对象转换为一个其他类型的数据(例如将一个Complex类对象转换成double类型数据)。 C++提供类型转换函数(type conversion function)来解决这个问题

    一般形式

    operator 类型名()

    {实现转换的语句}

    注意:

    1.类型转换函数只能作为成员函数,因为转换的主体是本类对象,不能作为友元函数或普通函数

    2.从函数形式可以看到,它与运算符重载函数相似,都是用关键字operator开头,只是被重载的是类型名

    3.程序中的Complex类对象是不是一律都转换成为double类型数据?否,Complex对象既是Complex对象,也可以作为double类型数据,需要时才进行转换

    针对最前面那个复数类的例子,如果在类中定义:

    operator double(){return real;}

    在main函数中如果有:

    Complex a(1,2);

    double d=a+2.5;

    此时在没有特定运算符重载函数的情况下,编译器将会自动把a转换为double和2.5相加

    假如程序中需要对一个Complex类对象和一个double型变量进行+,-,*,/等算术运算,以及关系运算和逻辑运算,如果不用类型转换函数,就要对多种运算符进行重载,以便能进行各种运算 如果用类型转换函数对double进行重载(使Complex类对象转换为double型数据),就不必对各种运算符进行重载,因为Complex类对象可以被自动地转换为double型数据,而标准类型的数据的运算,是可以使用系统提供的各种运算符的

    注意!如果同时有转换构造函数和类型转换函数,可能出现二义性!

    比如进行上述的double d=a+2.5;时,我到底是把a通过类型转换函数变成double呢,还是让2.5通过转换构造函数变为Complex呢???在使用的时候要注意这一点

  • 相关阅读:
    重新定义容器化 Serverless 应用的数据访问
    Jupyter 报错:can‘t convert np.ndarray of type numpy.object_.
    微软警告国家级黑客正在利用关键的Atlassian Confluence漏洞
    EMQX Operator 如何快速创建弹性伸缩的 MQTT 集群
    Redis 中 redis-benchmark 详解及参数介绍
    如何让bug远离你?
    乐观锁 or 悲观锁 你怎么选?
    POSTGIS数据库操作
    计算机组成原理---第五章中央处理器---控制器的功能和工作原理
    阿里云安全组 设置数据库仅自己电脑IP可登陆
  • 原文地址:https://blog.csdn.net/m0_63222058/article/details/134489428