C++面向对象(OOP)编程-运算符重载

本文主要介绍C++面向对象编程中的多态的手段之一运算符重载,讲清运算符重载的本质,以及通过代码实现一些常用的运算符重载。

目录

1 运算符重载的本质

2 运算符重载格式

3 运算符重载分类

3.1 重载为类的成员函数

3.1.1 双目运算符重载

3.1.2 单目运算符重载

3.2 重载为类的全局友元函数

3.3 两种重载方式对比

4 运算符重载规则  

5 一些运算符的重载

5.1 输入输出运算符的重载

5.2 关系运算符重载

5.3 赋值运算符的重载

5.4 类型转换运算符重载


1 运算符重载的本质

        运算符重载(operator overload)是对已有的常规运算符赋予多重含义,使用相同的运算符,通过提供操作不同数据类型实现不同的行为。由于运算符的本质是C++封装的类的成员函数,因此运算符重载的本质是函数重载。

        比如 对于运算符+,常规支持一般的int等数据类型,我们可以重载让其两个对象相加,赋予新的含义。

2 运算符重载格式

        

返回类型 operator 运算符(参数列表)

{

    函数体;

}

operator 为关键字,运算符重载只能重载C++允许的运算符。

3 运算符重载分类

3.1 重载为类的成员函数

        根据操作数的多少会分为单目运算符的重载和双目运算符的重载。双目运算符重载为类的成员函数,则它有两个操作数:左操作数是对象本身的数据,右操作数则通过运算符重载函数的参数表来传递。

3.1.1 双目运算符重载

        调用格式为:

                左操作数.运算符重载函数(右操作数)

        以下代码是运算符 - + * << 的重载:

#include <iostream>
#include <fstream>
class Complex
{
    private:
        double real;
        double imag;

    public:
        Complex(double r, double i) : real(r), imag(i) {}

        // 运算符+重载
        Complex operator+(const Complex &other) const
        {
            double newReal = real + other.real;
            double newImag = imag + other.imag;
            return Complex(newReal, newImag);
        }

        // 运算符-重载
        Complex operator - (const Complex & other) const
        {
            return Complex((real-other.real),(imag-other.imag));

        }

        // 运算符*重载
        Complex operator * (const Complex & other) const
        {
            return Complex((real * other.real),(imag * other.imag));
        }

        // 运算符<<重载,用于输出对象
        friend std::ostream & operator << (std::ostream &os, const Complex &c)
        {
            os << "(" << c.real << ", " << c.imag << ")";
            return os;
        }

};



int main()
{
    Complex a(1.0, 2.0);
    Complex b(3.0, 4.0);
    

    Complex c = a + b; // 使用运算符+进行复杂数相加

    std::cout << "a+b: " << c << std::endl;

    Complex d = a - b;

    std::cout << "a-b: " << d << std::endl;

    Complex f = a - b;

    std::cout << "a*b: " << f << std::endl;


    return 0;
}

运行结果:

3.1.2 单目运算符重载

        如果是单目运算符重载为类的成员函数,则要分为前置(++i)和后置(i++)运算符。

如果是前置运算符,则它的操作数是函数调用者,函数没有参数。

调用格式为:

操作数.运算符重载函数()        

        以下代码是运算符++i和i++的重载:

#include <iostream>
#include <fstream>
class Complex
{
    private:
        double real;
        double imag;

    public:
        Complex(double r, double i) : real(r), imag(i) {}

        // ++i
        Complex operator ++ ()
        {
            ++(this->real);
            ++(this->imag);
            return *this;
        }

        // i++

        Complex operator ++ (int)
        {
            Complex cpx = *this;
            (this->real)++;
            (this->imag)++;
            return cpx;

        }

        // 运算符<<重载,用于输出对象
        friend std::ostream & operator << (std::ostream &os, const Complex &c)
        {
            os << "(" << c.real << ", " << c.imag << ")";
            return os;
        }

};


int main()
{
    Complex a(1.0, 2.0);
    Complex b(3.0, 4.0);
    

    std::cout << "*************单目运算符重载i++*************" << std::endl;
    std::cout << "a: " << a << " a++: " << a++ << std::endl;
    std::cout << "a: " << a << " (a++): " << (a++) << std::endl;

    std::cout << "*************单目运算符重载++i*************" << std::endl;
    
    std::cout << "a: " << a << " ++a: " << ++a << std::endl;
    std::cout << "a: " << a << " (++a): " << (++a) << std::endl;

    return 0;
}

运行结果:

        实现前置“++”时,数据成员进行自增运算,然后返回当前对象(即this指针所指 向的对象)。实现后置“++”时,创建了一个临时对象来保存当前对象的值,然后再将当前对象自增,最后返回的是保存了初始值的临时对象。

        前置单目运算符和后置单目运算符的最主要区别是函数的形参,后置单目运 算符带一个int型形参,但它只起区分作用

3.2 重载为类的全局友元函数

        相比与重载为类的成员函数,重载为友元函数,只需要在其前面加friend`。

重载函数格式:

friend 返回类型 operator 运算符(参数列表)

{

    函数体;

}

调用格式

        operator 运算符(参数1,参数2)

        以下是运算符-的友元函数的重载:

#include <iostream>
#include <fstream>
class Complex
{
    private:
        double real;
        double imag;

    public:
        Complex(double r, double i) : real(r), imag(i) {}


        // 运算符<<重载,用于输出对象
        friend std::ostream & operator << (std::ostream &os, const Complex &c);
        // 友元函数 运算符重载-
        friend Complex operator - (const Complex & a, const Complex & b)
        {
            return Complex((a.real-b.real),(a.imag-b.imag));
        } 
};

std::ostream & operator<<(std::ostream &os, const Complex &c)
{
    os << "(" << c.real << ", " << c.imag << ")";
    return os;
}

int main()
{
    {
        Complex a(1.0, 2.0);
        Complex b(3.0, 4.0);

        std::cout << "*************全局函数为友元函数****************" << std::endl;

        Complex d = a - b;

        std::cout << "a-b: " << d << std::endl;
    }
    
    


    return 0;
}

        运行结果:

       

 运算符重载为类的友元函数时,由于没有隐含的this指针,因此,操作数的个数没有变化,所有的操作数都必须通过函数形参进行传递,函数的参数与操作数 自左自右保持一一对应,这是由友元函数的性质决定的。

3.3 两种重载方式对比

        一般运算符重载既可以为类的成员函数,也可以为全局友元函数。

一般原则:

        1,一般,单目运算符最好重载为类的成员函数,双目运算符最好重载为类的友元函数。

        2,若一个运算符的操作需要修改对象的状态,选择重载为成员函数较好。

        3,若运算符的操作数(尤其是第一个操作数)可能有隐式类型转换,则只能选用友元函 数。

        4,具有对称性的运算符可能转换任意一端的运算对象,如:算术(a+b和b+a)、关系运算 符(a>b和b<a)等,通常重载为友元函数。

        5,有4个运算符必须重载为类的成员函数:赋值=、下标[ ]、调用( )、成员访问->。

4 运算符重载规则  

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

        2,重载后的运算符的优先级、结合性也应该保持不变,也不能改变其操作个数和语 法结构。

        3,重载后的含义,与操作基本数据类型的运算含义应类似,如加法运算符“+”,重 载后也应完成数据的相加操作。

        4,有5个运算符不可重载:类关系运算符“:”、成员指针运算符“*”、作用域运算 符“::”、sizeof运算符、三目运算符“?:”

        5,运算符重载函数不能有默认参数,否则就改变了运算符操作数的个数,是错误的。

        6,运算符重载函数既可以作为类的成员函数,也可以作为类的友元函数(全局函数)。 

作为全局函数时,一般都需要在类中将该函数声明为友元函数。因为该函数大部分情况下都需要使用类的private成员。作为类的成员函数时,二元运算符的参数只有一个, 一元运算符不需要参数(参数没有任何意义,只是为了区分是前置还是后置形式:带 一个整型参数为后置形式)。因为有个参数(左操作数)是隐含的(隐式访问this指针 所指向的当前对象)。作为全局函数时,二元操作符需要两个参数,一元操作符需要一 个参数,而且其中必须有一个参数是对象,好让编译器区分这是程序员自定义的运算符, 防止程序员修改用于内置类型的运算符的性质。

5 一些运算符的重载

5.1 输入输出运算符的重载

friend istream& operator>>(istream& is, Person& p); // 输入运算符重载

friend ostream& operator<<(ostream& os, const Person& p); // 输出运算符重载

 代码如下:

#include <iostream>
using namespace std;

class Person {
public:
    Person() : name(""), age(0) {}
    
    friend istream& operator>>(istream& is, Person& p);
    friend ostream& operator<<(ostream& os, const Person& p);

private:
    string name;
    int age;
};

istream& operator>>(istream& is, Person& p) {
    cout << "Enter name: ";
    is >> p.name;
    cout << "Enter age: ";
    is >> p.age;
    return is;
}

ostream& operator<<(ostream& os, const Person& p) {
    os << "Name: " << p.name << ", Age: " << p.age;
    return os;
}

int main() {
    Person person;
    cin >> person; // 从控制台读取数据
    cout << person << endl; // 输出:Name: John, Age: 25
    return 0;
}

        运行结果:

5.2 关系运算符重载

        关系运算符的重载通常用于实现自定义的数据类型。例如,我们可以为一个类定义一个大于、小于和等于运算符的重载函数。

        (1)关系运算符都要成对的重载。如:重载了“<”运算符,就要重载“>” 运算符,反之亦然。

        (2)“==”运算符应该具有传递性。如:a==b,b==c,则a==c成立。

        (3)当成对出现运算符重载时,可以把一个运算符的工作委托给另一个运算 符。如:重载“!=”运算符是在“==”运算符的基础上,重载“<”运算 符 由重载过的“>”运算符来实现。

代码如下:

#include <iostream>
using namespace std;

class Person {
public:
    Person(const string& n, int a) : name(n), age(a) {}
    
    friend bool operator>(const Person& p1, const Person& p2);
    friend bool operator<(const Person& p1, const Person& p2);
    friend bool operator==(const Person& p1, const Person& p2);

private:
    string name;
    int age;
};

bool operator>(const Person& p1, const Person& p2) {
    return p1.age > p2.age;
}

bool operator<(const Person& p1, const Person& p2) {
    return p1.age < p2.age;
}

bool operator==(const Person& p1, const Person& p2) {
    return p1.age == p2.age;
}

int main() {
    Person person1("John", 25);
    Person person2("Jane", 30);
    
    cout << "person1 > person2: " << (person1 > person2) << endl; // 输出:person1 > person2: 1
    cout << "person1 < person2: " << (person1 < person2) << endl; // 输出:person1 < person2: 1
    cout << "person1 == person2: " << (person1 == person2) << endl; // 输出:person1 == person2: 0
    
    return 0;
}

        运行结果:

5.3 赋值运算符的重载

        若类中没有显式定义赋值运算符的重载函数,编译器将自动提供一个默认的重载函 数,完成数据成员的“浅拷贝”。若类中的数据成员上需要附加额 外的信息,比如类中存在指针成员,指针成员指向新开辟的空间,此时需要重载赋值运算符,以实现“深拷贝”。

注意:赋值运算符只能被重载为类的成员函数

        赋值运算符的重载可以实现函数的深拷贝,深拷贝也可以通过深拷贝构造函数实现。

代码实现:

#include <iostream>
using namespace std;
 
class A
{
private:
	int x, y;

public:
	A(int x1 = 0, int y1 = 0)
	{
		x = x1;
		y = y1;
	}
    A & operator = (const A & other)
    {
        this->x = other.x;
        this->y = other.y;
        return *this;
    }
        
	void show()
	{
		cout << "x=" << x << "," << "y=" << y << endl;
	}
};
int main()
{
	A a1(66, 2);
    A a2(97,3);
    a1 = a2;
	a1.show();
	return 0;
}

        运行结果:

5.4 类型转换运算符重载

        C++的一般基本的数据类型的强制转换为static_cast<data_type>(expression)

        重载类型转换运算符的格式:

operator 类型名()

{

        return static_cast<类型名>(expression)

};

        注意:

        (1)通过类型转换函数可以将自定义类型对象转换为基本类型数据。

        (2)类型转换函数被重载的是类型名。在函数名前不能指定函数类型,函数也没 有参数。返回值类型是由函数名中指定的类型名来确定的(如果类型名为double, 则将类型数据转换为double类型返回)。

        (3)因为函数转换的主体是本类的对象,类型转换运算符重载函数只能作为类的 成员函数。

代码如下:

#include <iostream>
using namespace std;
 
class A
{
private:
	int x, y;

public:
	A(int x1 = 0, int y1 = 0)
	{
		x = x1;
		y = y1;
	}
	operator char()
	{
		return static_cast<char>(x);
	}
	void show()
	{
		cout << "x=" << x << "," << "y=" << y << endl;
	}
};
int main()
{
	A a1(66, 2);
	a1.show();
	char ch = a1;
	cout << ch << endl;
	return 0;
}

        运行结果:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Faulhaber 2.5代运动控制系统 25mNm/13W

2.5代控制系统&#xff1b; PWM输出&#xff1b; 四象限控制带&#xff1b; RS232或CANopen通信接口&#xff1b; 2250_BX4_CxD 选件&#xff0c;电缆和连接信息&#xff1a; 适配部件&#xff1a;

Java-File类与IO流(1)

我是南城余&#xff01;阿里云开发者平台专家博士证书获得者&#xff01; 欢迎关注我的博客&#xff01;一同成长&#xff01; 一名从事运维开发的worker&#xff0c;记录分享学习。 专注于AI&#xff0c;运维开发&#xff0c;windows Linux 系统领域的分享&#xff01; 本…

在4*4的平面上计算2a1+1+1

0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 在4*4的平面上有2个点&#xff0c;保持2a1的结构&#xff0c;然后向剩余的14个格子里随机扔2个石子。 共有14*13/291种可能 1 - - - 2 - - - 3 - - 1 4 - - - 1 1 - 1 1 - - - - - - - 1 - - …

Python个人代码随笔(观看无益,请跳过)

异常抛错&#xff1a;一般来说&#xff0c;在程序中&#xff0c;遇到异常时&#xff0c;会从这一层逐层往外抛错&#xff0c;一直抛到最外层&#xff0c;由最外层把错误显示在用户终端。 try:raise ValueError("A value error...") except ValueError:print("V…

IS-IS原理与配置3

IS-IS原理与配置 • IS-IS&#xff08;Intermediate System to Intermediate System&#xff0c;中间系统到中间系统&#xff09;是ISO &#xff08;International Organization for Standardization&#xff0c;国际标准化组织&#xff09;为它的CLNP &#xff08;ConnectionL…

FGSM、PGD、BIM对抗攻击算法实现

本篇文章是博主在AI、无人机、强化学习等领域学习时&#xff0c;用于个人学习、研究或者欣赏使用&#xff0c;并基于博主对人工智能等领域的一些理解而记录的学习摘录和笔记&#xff0c;若有不当和侵权之处&#xff0c;指出后将会立即改正&#xff0c;还望谅解。文章分类在AI学…

Unity中 URP Shader 的纹理与采样器的分离定义

文章目录 前言一、URP Shader 纹理采样的实现1、在属性面板定义一个2D变量用于接收纹理2、申明纹理3、申明采样器4、进行纹理采样 二、申明纹理 和 申明采样器内部干了什么1、申明纹理2、申明采样器 三、采样器设置采样器的传入格式1、纹理设置中&#xff0c;可以看见我们的采样…

vue脚手架安装及使用

准备工作 安装node安装cnpm cnpm是npm的“廉价平替” 提高安装速度 npm install -g cnpm --registryhttps://registry.npm.taobao.org 安装脚手架 安装Vue脚手架 cnpm install -g vue/cli 用vue脚手架创建vue项目 找好创建项目的位置 创建项目 vue create test (test为项…

鸿蒙系统(HarmonyOS)之方舟框架(ArkUI)介绍

鸿蒙开发官网&#xff1a;HarmonyOS应用开发官网 - 华为HarmonyOS打造全场景新服务 方舟开发框架&#xff08;简称&#xff1a;ArkUI&#xff09;&#xff0c;是一套构建HarmonyOS应用界面的UI开发框架&#xff0c;它提供了极简的UI语法与包括UI组件、动画机制、事件交互等在内…

CSS文本样式(详解)

CSS文本样式 &#x1f367; 文本颜色&#x1f9c1;文本缩进&#x1f368;文本对齐&#x1f365;文本行高&#x1f95d;文本装饰 &#x1f367; 文本颜色 属性&#xff1a;color 作用&#xff1a;设置文本颜色 属性值&#xff1a; 颜色表示方式表示含义属性值颜色名称预定义的…

【专题】最小生成树(prim算法、kruscal算法)

目录 一、最小生成树二、Prim算法1. 算法思想2. 例题3. 性能分析 三、Kruscal算法1. 算法思想2. 例题3. 性能分析 一、最小生成树 生成树中边的权值&#xff08;代价&#xff09;之和最小的树。 二、Prim算法 1. 算法思想 设N(V,{E})是连通网&#xff0c;TE是N上最小生成树…

目前最火的大模型训练框架 DeepSpeed 详解来了

目前&#xff0c;大模型的发展已经非常火热&#xff0c;关于大模型的训练、微调也是各个公司重点关注方向&#xff0c;但是大模型训练的痛点是模型参数过大&#xff0c;动辄上百亿&#xff0c;如果单靠单个GPU来完成训练基本不可能。所以需要多卡或者分布式训练来完成这项工作。…

虚拟机启动 I/O error in “xfs_read_agi+0x95“

1.在选择系统界面按e 进入维护模式 2.找到ro把ro改成 rw init/sysroot/bin/sh 然后按Ctrlx 3.找到坏掉的分区&#xff0c;以nvme0n1p3为例进行修复 xfs_repair -d /dev/nvme0n1p3 4.init 6 重新启动 以下情况 先umount 再修复 则修复成功

《使用ThinkPHP6开发项目》 - 登录接口一

《使用ThinkPHP6开发项目》 - 安装ThinkPHP框架-CSDN博客 《使用ThinkPHP6开发项目》 - 设置项目环境变量-CSDN博客 《使用ThinkPHP6开发项目》 - 项目使用多应用开发-CSDN博客 《使用ThinkPHP6开发项目》 - 创建应用-CSDN博客 《使用ThinkPHP6开发项目》 - 创建控制器-CSD…

Rabbitmq消息重复消费问题(幂等性保障)

消息百分百投递架构 在《消息可靠性保证》篇章中&#xff0c;我通过生产者确认机制保障了消息会发送到MQ中&#xff0c;但是在生产者与MQ建立过程的时候出现了网络抖动&#xff0c;连接建立失败&#xff0c;生产者就感知不到MQ返回的ack/nack&#xff0c;无法完全保障消息投递…

配电室环境智能监测系统

配电室环境智能监测系统是一种先进的在线监测系统&#xff0c;依托电易云-智慧电力物联网&#xff0c;用于实时监测配电室内部的环境参数&#xff0c;包括温度、湿度、SF6气体浓度、烟雾浓度等。该系统具有以下功能特点&#xff1a; 实时监测&#xff1a;系统能够实时监测配电室…

windows wsl2 ubuntu上部署 redroid云手机

Redroid WSL2部署文档 下载wsl内核源码 #文档注明 5.15和5.10 版本内核可以部署成功&#xff0c;这里我当前最新的发布版本 #下载wsl 源码 wget --progressbar:force --output-documentlinux-msft-wsl-5.15.133.1.tar.gz https://codeload.github.com/microsoft/WSL2-Linux-Ker…

nginx 1.24.0 安装nginx最新稳定版

1.官网&#xff1a; nginx: download 2. 选择稳定版&#xff1a; 3. 可以下载&#xff0c;然后上传服务器&#xff0c;也可以wget获取&#xff1a; cd /home wget https://nginx.p2hp.com/download/nginx-1.24.0.tar.gz 4. 放入/home 下。并解压缩&#xff0c;重命名nginx;…

基于ssm电影网站源码和论文

随着Internet的发展&#xff0c;人们的日常生活已经离不开网络。未来人们的生活与工作将变得越来越数字化&#xff0c;网络化和电子化。它将是直接管理电影网站的最新形式。本论文是以构建电影网站为目标&#xff0c;使用 java技术制作&#xff0c;由管理员和用户两大部分组成。…

R语言|分面中嵌入趋势线

简介 关于分面的推文&#xff0c;小编根据实际科研需求&#xff0c;已经分享了很多技巧。例如&#xff1a; 分面中添加不同表格 分面中添加不同的直线 基于分面的面积图绘制 分面中的细节调整汇总 基于分面的折线图绘制 最近科研中又遇到了与分面相关的需求&#xff1a;…