C++ 运算符重载详解

news2024/10/7 16:22:24

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

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

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

#include <iostream>
using namespace std;
class Complex                                     //定义Complex类
{public:
Complex( ){real=0;imag=0;}                      //定义构造函数
Complex(double r,double i){real=r;imag=i;}     //构造函数重载
Complex complex_add(Complex &c2);              //声明复数相加函数
void display( );                                //声明输出函数
 private:
double real;                                   //实部
double imag;                                   //虚部
};
Complex Complex∷complex_add(Complex &c2)
{	Complex c;
	c.real=real+c2.real;
	c.imag=imag+c2.imag;
	return c;
}   
void Complex∷display( )                            //定义输出函数
{cout<<″(″<<real<<″,″<<imag<<″i)″<<endl;}
int main( )
{
Complex c1(3,4),c2(5,-10),c3;                     //定义3个复数对象
c3=c1.complex_add(c2);                            //调用复数相加函数
cout<<″c1=″; c1.display( );                        //输出c1的值
cout<<″c2=″; c2.display( );                        //输出c2的值
cout<<″c1+c2=″; c3.display( );                     //输出c3的值
return 0;
}

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

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

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

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

定义重载函数: 在类或对象中定义与选择的运算符相关的特定方法。这些方法通常被称为运算符重载函数。在许多语言中,这些函数具有特殊的名称,例如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++标准类型或者是一个其它类的对象,则运算符重载函数不能作为成员函数,只能作为非成员函数,如果函数需要访问类的私有成员,则必须声明为友元函数

举个例子:

#include <iostream>
using namespace std;
class Complex
{public:
Complex( ){real=0;imag=0;}
Complex(double r,double i)
{real=r;imag=i;}
friend Complex operator +
 (Complex &c1,Complex &c2);
//重载函数作为友元函数
void display( );
 private:
double real;
double imag;
};
Complex operator + (Complex &c1,Complex &c2)         
 //定义作为友元函数的重载函数
{return Complex(c1.real+c2.real, c1.imag+c2.imag);}
void Complex∷display( )
{cout<<″(″<<real<<″,″<<imag<<″i)″<<endl;}

int main( )
{Complex c1(3,4),c2(5,-10),c3;
c3=c1+c2;
cout<<″c1=″; c1.display( );
cout<<″c2=″; c2.display( );
cout<<″c1+c2 =″; c3.display( );}

单目运算符的重载

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

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

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

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

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

举个例子:

#include <iostream>

class Counter {
private:
    int count;

public:
    Counter() : count(0) {}

    // 重载前置自增运算符 (++var)
    Counter& operator++() {
        count++;
        return *this;  // 返回递增后的对象引用
    }

    // 重载后置自增运算符 (var++)
    Counter operator++(int) {
        Counter temp(*this);  // 创建一个副本用于保存递增前的值
        count++;
        return temp;  // 返回递增前的对象副本
    }

    void display() const {
        std::cout << "Count: " << count << std::endl;
    }
};

int main() {
    Counter myCounter;

    // 使用前置自增运算符
    ++myCounter;
    myCounter.display();

    // 使用后置自增运算符
    Counter anotherCounter = myCounter++;
    myCounter.display();
    anotherCounter.display();

    return 0;
}

重载“<<”和">>"

格式:

重载"<<"

#include <iostream>

class Point {
private:
    int x, y;

public:
    Point(int xCoord, int yCoord) : x(xCoord), y(yCoord) {}

    // 重载输出运算符
    friend std::ostream& operator<<(std::ostream& out, const Point& point);
std::ostream& operator<<(std::ostream& out, const Point& point) {
    out << "Point(" << point.x << ", " << point.y << ")";
    return out;
}
int main() {
    Point myPoint(3, 4);

    // 使用重载的输出运算符
    std::cout << "My Point: " << myPoint << std::endl;

    return 0;
}

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

可以满足链式编程,实现类似:cout<<myPoint1<<myPoint2<<endl;的输出

下面看对">>"的重载

#include <iostream>

class Point {
private:
    int x, y;

public:
    Point() : x(0), y(0) {}

    // 重载输入运算符
    friend std::istream& operator>>(std::istream& in, Point& point);

    // 用于显示坐标的成员函数
    void display() const {
        std::cout << "Point(" << x << ", " << y << ")";
    }
};
std::istream& operator>>(std::istream& in, Point& point) {
    std::cout << "Enter x-coordinate: ";
    in >> point.x;
    std::cout << "Enter y-coordinate: ";
    in >> point.y;
    return in;
}

int main() {
    Point myPoint;

    // 使用重载的输入运算符
    std::cout << "Please enter coordinates for a point:\n";
    std::cin >> myPoint;

    // 使用成员函数显示坐标
    std::cout << "You entered: ";
    myPoint.display();
    std::cout << std::endl;

    return 0;
}

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

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

class Celsius {
private:
    double temperature;

public:
    // 转换构造函数,将double类型转换为Celsius对象
    Celsius(double temp) : temperature(temp) {}

    void display() {
        std::cout << "Temperature in Celsius: " << temperature << " C" << std::endl;
    }
};

int main() {
    // 使用转换构造函数,将double类型转换为Celsius对象
    Celsius celsiusObject = 25.5;
    celsiusObject.display();

    return 0;
}

类型转换函数:用转换构造函数可以将一个指定类型的数据转换为类的对象。但是不能反过来将一个类的对象转换为一个其他类型的数据(例如将一个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呢???在使用的时候要注意这一点

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1226958.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RabbitMQ消息的可靠性

RabbitMQ消息的可靠性 一 生产者的可靠性 生产者重试 有时候由于网络问题&#xff0c;会出现连接MQ失败的情况&#xff0c;可以配置重连机制 注意&#xff1a;SpringAMQP的重试机制是阻塞式的&#xff0c;重试等待的时候&#xff0c;当前线程会等待。 spring:rabbitmq:conne…

MySQL 的执行原理(四)

5.5. MySQL 的查询重写规则 对于一些执行起来十分耗费性能的语句&#xff0c;MySQL 还是依据一些规则&#xff0c;竭尽全力的把这个很糟糕的语句转换成某种可以比较高效执行的形式&#xff0c;这个过程也可以 被称作查询重写。 5.5.1. 条件化简 我们编写的查询语句的搜索条件…

【STM32】ADC(模拟/数字转换)

一、ADC的简介 1.什么是ADC 1&#xff09;将【电信号】-->【电压】-->【数字量】 2&#xff09;ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字量&#xff0c;建立模拟电路到数字电路的桥梁。 3&#xff09;12位逐次逼近型ADC&#xff0c;1us转换时间&#xf…

iOS_折叠展开 FoldTextView

1. 显示效果 Test1&#xff1a;直接使用&#xff1a; Test2&#xff1a;在 cell 里使用&#xff1a; 2. 使用 2.1 直接使用 // 1.1 init view private lazy var mooFoldTextView: MOOFoldTextView {let view MOOFoldTextView(frame: .zero)view.backgroundColor .cyanvie…

Node.js之fs文件系统模块

什么是fs文件系统模块&#xff1f;又如何使用呢&#xff1f;让我为大家介绍一下&#xff01; fs 模块是 Node.js 官方提供的、用来操作文件的模块。它提供了一系列的方法和属性&#xff0c;用来满足用户对文件的操作需求 注意&#xff1a;如果要在JavaScript代码中&#xff0c…

Linux 网络:PMTUD 简介

文章目录 1. 前言2. Path MTU Discovery(PMTUD) 协议2.1 PMTUD 发现最小 MTU 的过程 3. Linux 的 PMTUD 简析3.1 创建 socket 时初始化 PMTUD 模式3.2 数据发送时 PMTUD 相关处理3.2.1 源头主机发送过程中 PMTU 处理3.2.2 转发过程中 PMTUD 处理 4. PMTUD 观察5. 参考链接 1. 前…

k8s的高可用集群搭建,详细过程实战版

kubernetes高可用集群的搭建 前面介绍过了k8s单master节点的安装部署 今天介绍一下k8s高可用集群搭建 环境准备&#xff1a; vip &#xff1a;192.168.121.99 keeplive master01&#xff1a;192.168.121.153 centos7 master02&#xff1a;192.168.121.154 centos7 master03&a…

Ubuntu20.0中安装Gradle

下载Gradle到temp文件夹 wget https://services.gradle.org/distributions/gradle-8.3-bin.zip -P /tmp 然后解压文件到/opt/gradle目录 sudo unzip -d /opt/gradle /tmp/gradle-8.3.zip 配置Gradle环境变量 接下来我们会创建一个gradle.sh文件来保存Gradle的环境变量 sudo…

图像分类(六) 全面解读复现MobileNetV1-V3

MobileNetV1 前言 MobileNetV1网络是谷歌团队在2017年提出的&#xff0c;专注于移动端和嵌入设备的轻量级CNN网络&#xff0c;相比于传统的神经网络&#xff0c;在准确率小幅度降低的前提下大大减少模型的参数与运算量。相比于VGG16准确率减少0.9%&#xff0c;但模型的参数只…

云存储与物理存储:优缺点对比分析

当您需要存储数字文件时&#xff0c;您有两个基本选择&#xff1a;云存储和物理存储。 云存储允许您通过互联网将文件保存在云存储提供商运营的服务器上。这些公司通常在多个数据中心制作文件的备份副本&#xff0c;并使用复杂的加密来保护它们。您可以从任何连接互联网的设备访…

Docker中快速安装RabbitMQ

文章目录 前言一、安装Docker二、安装RabbitMQ无脑命令行运行 总结 前言 在Ubuntu中的Docker容器中快速安装RabbitMQ&#xff0c;亲测有效&#xff0c;不废话&#xff0c;上操作。 一、安装Docker 直接按照Docker官方教程操作&#xff1a;官方安装教程 点进官网&#xff0c;往…

重命名com1.{d3e34b21-9d75-101a-8c3d-00aa001a1652}文件夹

今天在win10系统上&#xff0c;发现一个名称为: com1.{d3e34b21-9d75-101a-8c3d-00aa001a1652} 的文件夹&#xff0c;该文件夹很奇怪&#xff0c;既不能手动删除&#xff0c;也不能手动给文件夹重命名&#xff0c;如图(1)所示&#xff1a; E:\EncodeOne\hello\Thumbs.ms\com1.…

Nodejs中net模块多次Socket.setTimeout无法覆盖之前函数,导致叠加执行问题解决

Hi, I’m Shendi Nodejs中net模块多次Socket.setTimeout无法覆盖之前函数&#xff0c;导致叠加执行问题解决 问题描述 在 Nodejs 中&#xff0c;net 模块的 Socket 的 setTimeout 函数是设置超时时间&#xff0c;如果多次设置&#xff0c;超时时间会是最后一次的时间&#xff…

RepVgg: 网络结构重参化

CVPR2021 截至目前1004引 论文连接 代码连接 文章提出的问题 大多数的研究者追求的是设计一个好的网络结构,这种“好”体现在网络具有复杂的网络设计,这种网络虽然比简单的网络收获了更加高的准确率,但是网络结构中的大量并行分支,导致模型的难以应用和自定义,主要体现…

PC业务校验(已有该名称,已有该编码)

rules: {name: [{ required: true, message: "部门名称不能为空", trigger: "blur" },{min: 2,max: 10,message: "部门名称的长度为2-10个字符",trigger: "blur",},{trigger: "blur",validator: async (rule, value, callba…

算法通关村第十一关-青铜挑战理解位运算的规则

大家好我是苏麟 , 今天聊聊位运算 . 位运算规则 计算机采用的是二进制&#xff0c;二进制包括两个数码:0&#xff0c;1。在计算机的底层&#xff0c;一切运算都是基于位运算实现的&#xff0c;所以研究清整位运算可以加深我们对很多基础原理的理解程度。 在算法方面&#xf…

HTML5+CSS3+JS小实例:使用L2Dwidget实现二次元卡通看板娘

实例:使用L2Dwidget实现二次元卡通看板娘 技术栈:HTML+CSS+JS 效果: 源码: <!DOCTYPE html> <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport" conte…

YOLOv8优化策略:轻量级Backbone改进 | 高效模型 (Efficient MOdel, EMO),现代倒残差移动模块设计 | ICCV2023

🚀🚀🚀本文改进:面向移动端的轻量化网络模型——EMO,它能够以相对较低的参数和 FLOPs 超越了基于 CNN/Transformer 的 SOTA 模型,支持四个版本EMO_1M, EMO_2M, EMO_5M, EMO_6M 🚀🚀🚀YOLOv8改进专栏:http://t.csdnimg.cn/hGhVK 学姐带你学习YOLOv8,从入门到…

【Python从入门到进阶】42、使用requests的Cookie登录古诗文网站

接上篇《41、有关requests代理的使用》 上一篇我们介绍了requests代理的基本使用&#xff0c;本篇我们来学习如何利用requests的Cookie登录古诗文网。 一、登录网站及目的介绍 我们需要Cookie模拟登录的网站为&#xff1a;https://www.gushiwen.cn/&#xff08;古诗文网&…

Hive调优

1.参数配置优化 设定Hive参数有三种方式&#xff1a; &#xff08;1&#xff09;配置Hive文件 当修改配置Hive文件的设定后&#xff0c;对本机启动的所有Hive进程都有效&#xff0c;因此配置是全局性的。 一般地&#xff0c;Hive的配置文件包括两部分&#xff1a; a&#xff…