【C++面向对象】封装(下):探索C++运算符重载设计精髓

🔥个人主页 🔥

😈所属专栏😈 


每文一诗  💪🏼

     年年岁岁花相似,岁岁年年人不同 —— 唐/刘希夷《代悲白头翁》

        译文:年年岁岁繁花依旧,岁岁年年看花之人却不相同


目录

C++运算符重载概念

C++运算符重载语法及作用

加减乘除运算符重载

赋值运算符重载

返回值作为引用以实现链式赋值

关系运算符重载

大于号运算符重载

相等运算符重载

全部代码


C++运算符重载概念

        在 C++ 里,运算符重载属于多态的一种表现形式,它允许你为自定义的数据类型重新定义运算符的行为。

运算符重载其实就是对已有的运算符赋予新的功能,使它能够处理自定义类型的对象

C++运算符重载语法及作用

语法:返回类型 operator运算符(参数列表) {}

  1. 提高代码可读性:在处理自定义类型时,使用重载后的运算符能让代码更直观、自然。例如,对于自定义的复数类,你可以重载+运算符,让两个复数相加的操作就像普通数字相加一样简单。
  2. 实现自定义类型的操作:借助运算符重载,你能为自定义类型实现像内置类型那样的操作。比如,对于自定义的矩阵类,你可以重载+-*等运算符,实现矩阵的加减乘运算。
  3. 代码复用与一致性:运算符重载可以复用已有的运算符,使代码更具一致性。对于熟悉内置运算符的开发者来说,重载后的运算符也易于理解和使用。

加减乘除运算符重载

  1. 作类成员函数重载
#include <iostream>class human
{
public:human(){};human(int a ,int b):m_a(a),m_b(b){}//加减乘除运算符重载(成员函数)human operator*(const human& h){human h2;h2.m_a = this->m_a * h.m_a;h2.m_b = this->m_b * h.m_b;return h2;}int geta(){return this->m_a;}int getb(){return this->m_b;}private:int m_a;int m_b;};int main(int argc, char const *argv[])
{human h(10,20);human h1(2,10);//相当于 h.operator*(h1)human h3 = h * h1;std::cout << h3.geta() <<" "<< h3.getb()<<std::endl;return 0;
}

代码解读:

  • 提供默认无参构造函数函数初始化列表
     human(){};human(int a ,int b):m_a(a),m_b(b){}
  • 加减乘除运算符重载(成员函数)这里指的是乘法
    human operator*(const human& h){human h2;h2.m_a = this->m_a * h.m_a;h2.m_b = this->m_b * h.m_b;return h2;}
  1. 这里的参数是指常量引用目的是不使用值传递的方式,防止对数据进行大量的拷贝,常量引用本质使用地址传递,地址通常占有4个字节,程序效率更高
  2. 这里的this指向调用该重载函数的哪个对象。
  3. 栈区重新创建一个对象h,并将运算后的成员变量更新到该对象并返回。
  • human h3 = h * h1;

    这个相当于 h.operator*(h1)

  • std::cout << h3.geta() <<" "<< h3.getb()<<std::endl;

    因为成员变量m_a和m_b时私有变量,不能在类外访问,所以在类内使用函数来访问。

  • 输出

这样就实现了类型为human的变量使用运算符来进行运算,20 = 10 *2 ;200 = 20*10

其他加减除法以此类推。

2.作全局函数重载

#include <iostream>class human
{friend human operator*(const human& p2, int val);
public:human(){};human(int a ,int b):m_a(a),m_b(b){}//加减乘除运算符重载(成员函数)human operator*(const human& h){human h2;h2.m_a = this->m_a * h.m_a;h2.m_b = this->m_b * h.m_b;return h2;}int geta(){return this->m_a;}int getb(){return this->m_b;}private:int m_a;int m_b;};
// 加减乘除运算符重载(全局函数)访问私有成员需要加friend声明
human operator*(const human& p2, int val)
{human temp;temp.m_a = p2.m_a *val;temp.m_b = p2.m_b * val;return temp;
}int main(int argc, char const *argv[])
{human h(10,20);human h1(2,10);//相当与 operator*(h,90)human h2 = h * 90;//相当于 h.operator*(h1)human h3 = h * h1;std::cout << h2.geta() <<" "<< h2.getb()<<std::endl;std::cout << h3.geta() <<" "<< h3.getb()<<std::endl;return 0;
}

代码解读:

  •  加减乘除运算符重载(全局函数)访问私有成员需要加friend声明
human operator*(const human& p2, int val)
{human temp;temp.m_a = p2.m_a *val;temp.m_b = p2.m_b * val;return temp;
}

        该函数在类外定义,在类外定义的函数不能访问类内的私有变量,但是可以通过友元函数的方法来让类外定义的全局函数访问类内的私有变量。即

    friend human operator*(const human& p2, int val);
  • human h2 = h * 90;

     相当与 operator*(h,90)调用的是全局函数。

  • 输出

赋值运算符重载

返回值作为引用以实现链式赋值

#include <iostream>class human
{public:human(){};human(int a ,int b,int c):m_a(a),m_b(b),m_c(new int(c)){}//赋值运算符重载human& operator=(const human& h){if(this != &h){if(m_c != nullptr){delete m_c;m_c = nullptr;}}m_c = new int(*h.m_c);return *this;}int geta(){return this->m_a;}int getb(){return this->m_b;}int getc(){return *this->m_c;}
private:int m_a;int m_b;int* m_c;};int main(int argc, char const *argv[])
{human h(10,20,12);human h1(2,10,34);human h2(22,10,4);h = h1 = h2;std::cout <<h.getc()<<std::endl;return 0;
}

 代码解读:

  • 函数初始化列表来初始化成员变量

        human(int a ,int b,int c):m_a(a),m_b(b),m_c(new int(c)){}
  • 赋值运算符重载

     human& operator=(const human& h){if(this != &h){if(m_c != nullptr){delete m_c;m_c = nullptr;}}m_c = new int(*h.m_c);return *this;}

          首先使用this来判断调用该函数的对象是否和传入的对象是同一块内存,接着在判断调用该成员函数的对象中的成员变量m_c是否不为空,不为空则释放内存,再置为空。

两个对象不是同一块内存(this != &h)需要delete,再重新开辟内存。

  • 这样作的目的是用于:如果对一个成员变量是指针的对象,将该指针指向的值进行修改时,需要先释放调用该重载函数的对象中指针变量,再为空,然后在m_c = new int(*h.m_c);重新开辟一块内存,内存的值是参数中对象的那个值。

  • 原因:若不释放这块内存,在赋值操作完成后,原来的内存就会变成无法访问的 “孤儿” 内存,程序无法再释放它,从而造成内存泄漏。执行 delete m_c; 就能正确释放这块内存,让系统可以重新使用它。

两个对象是同一块内存(this == &h)不需要delete。

  • 原因:当两个对象是统一块内存时,m_c这个指针变量指向的值是一样的,如果对其执行 delete m_c,那么在后面m_c = new int(*h.m_c);时使用*h.m_c时是非法的,因为这块内存已经被释放掉了,不能访问。

为什么要m_c = new int(*h.m_c);

  • 原因:编译器默认提供的是浅拷贝,对于一个含有指针变量的类中,浅拷贝会对这块内存多次释放,是不合法的,所以需要执行深拷贝来使得两个对象中的指针变量指向的值相同,但指针的值不同(地址不同)
  • 有关【C++面向对象】封装(上):探寻构造函数的幽微之境-CSDN博客深拷贝,浅拷贝【C++面向对象】封装(上):探寻构造函数的幽微之境-CSDN博客

为什么return *this;返回值类型是human&引用。

  • 原因:this指向调用该成员函数的那个对象,而*this是指的该对象,而返回值是human&引用是为了实现链式调用,而返回值是human不能实现链式赋值,因为他返回的是该对象的拷贝。

关系运算符重载

大于号运算符重载

#include <iostream>class human
{friend bool operator>(const human& h1,int val);
public:human(){};human(int a ,int b,int c):m_a(a),m_b(b),m_c(new int(c)){}//关系运算符重载bool operator>(const human& h1){if(this->m_a > h1.m_a && this->m_b > h1.m_b)return true;elsereturn false;}int geta(){return this->m_a;}int getb(){return this->m_b;}int getc(){return *this->m_c;}
private:int m_a;int m_b;int* m_c;};//关系运算符重载(全局函数)访问私有成员需要加friend声明
bool operator>(const human& h1,int val){if(val< h1.m_a && val < h1.m_b)return true;elsereturn false;
}
int main(int argc, char const *argv[])
{human h(10,20,12);human h1(2,10,34);//相当于 h.operator>(h1)if(h > h1)std::cout<<"h>h1"<<std::endl;elsestd::cout<<"h<h1"<<std::endl;//相当与 operator>(h,12)if(h > 12)std::cout<<"h>12"<<std::endl;elsestd::cout<<"h<12"<<std::endl;return 0;
}

 代码解读:

上段代码分别用成员函数和全局函数的方法来实现对自定义类型的判断

  • 对于
h > h1

相当于 h.operator>(h1)

  • 对于
h > 12

相当与 operator>(h,12)

  • 输出

相等运算符重载

#include <iostream>class human
{friend bool operator==(const human& h1,int val);
public:human(){};human(int a ,int b,int c):m_a(a),m_b(b),m_c(new int(c)){}//关系运算符重载bool operator==(const human& h1){if(this->m_a == h1.m_a && this->m_b == h1.m_b)return true;elsereturn false;}int geta(){return this->m_a;}int getb(){return this->m_b;}int getc(){return *this->m_c;}
private:int m_a;int m_b;int* m_c;};//关系运算符重载(全局函数)访问私有成员需要加friend声明
bool operator==(const human& h1,int val){if(val == h1.m_a)return true;elsereturn false;
}int main(int argc, char const *argv[])
{human h(10,20,12);human h1(2,10,34);//相当于 h.operator==(h1)if(h == h1)std::cout<<"二者相等"<<std::endl;elsestd::cout<<"二者不等"<<std::endl;//相当与 operator==(h,23)if(h == 23)std::cout<<"二者相等"<<std::endl;elsestd::cout<<"二者不等"<<std::endl;return 0;
}

  代码解读:

上段代码分别用成员函数和全局函数的方法来实现对自定义类型的相等判断

  • 对于
h == h1

相当于相当于 h.operator==(h1)

  • 对于
h == 23

相当于 operator==(h,23)

  • 输出

全部代码

#include <iostream>class human
{friend human operator*(const human& p2, int val);friend bool operator==(const human& h1,int val);friend bool operator>(const human& h1,int val);
public:human(){};human(int a ,int b,int c):m_a(a),m_b(b),m_c(new int(c)){}//加减乘除运算符重载(成员函数)human operator*(const human& h){human h2;h2.m_a = this->m_a * h.m_a;h2.m_b = this->m_b * h.m_b;return h2;}//关系运算符重载bool operator==(const human& h1){if(this->m_a == h1.m_a && this->m_b == h1.m_b)return true;elsereturn false;}//关系运算符重载bool operator>(const human& h1){if(this->m_a > h1.m_a && this->m_b > h1.m_b)return true;elsereturn false;}//赋值运算符重载human& operator=(const human& h){if(this != &h){if(m_c != nullptr){delete m_c;m_c = nullptr;}}m_c = new int(*h.m_c);return *this;}int geta(){return this->m_a;}int getb(){return this->m_b;}int getc(){return *this->m_c;}
private:int m_a;int m_b;int* m_c;};
// 加减乘除运算符重载(全局函数)访问私有成员需要加friend声明
human operator*(const human& p2, int val)
{human temp;temp.m_a = p2.m_a *val;temp.m_b = p2.m_b * val;return temp;
}//关系运算符重载(全局函数)访问私有成员需要加friend声明
bool operator==(const human& h1,int val){if(val == h1.m_a)return true;elsereturn false;
}
//关系运算符重载(全局函数)访问私有成员需要加friend声明
bool operator>(const human& h1,int val){if(val< h1.m_a && val < h1.m_b)return true;elsereturn false;
}
int main(int argc, char const *argv[])
{human h(10,20,12);human h1(2,10,34);//相当与 operator*(h,90)human h2 = h * 90;//相当于 h.operator*(h1)human h3 = h * h1;//相当于 h.operator==(h1)if(h == h1)std::cout<<"二者相等"<<std::endl;elsestd::cout<<"二者不等"<<std::endl;//相当与 operator==(h,23)if(h == 23)std::cout<<"二者相等"<<std::endl;elsestd::cout<<"二者不等"<<std::endl;//相当于 h.operator>(h1)if(h > h1)std::cout<<"h>h1"<<std::endl;elsestd::cout<<"h<h1"<<std::endl;//相当与 operator>(h,12)if(h > 12)std::cout<<"h>12"<<std::endl;elsestd::cout<<"h<12"<<std::endl;std::cout << h2.geta() <<" "<< h2.getb()<<std::endl;std::cout << h3.geta() <<" "<< h3.getb()<<std::endl;h = h1;std::cout <<h.getc()<<std::endl;return 0;
}

 🔥个人主页 🔥

😈所属专栏😈 

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

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

相关文章

从代码学习深度学习 - Transformer PyTorch 版

文章目录 前言1. 位置编码(Positional Encoding)2. 多头注意力机制(Multi-Head Attention)3. 前馈网络与残差连接(Position-Wise FFN & AddNorm)3.1 基于位置的前馈网络(PositionWiseFFN)3.2 残差连接和层规范化(AddNorm)4. 编码器(Encoder)4.1 编码器块(Enco…

阅读分析Linux0.11 /boot/head.s

目录 初始化IDT、IDTR和GDT、GDTR检查协处理器并设置CR0寄存器初始化页表和CR3寄存器&#xff0c;开启分页 初始化IDT、IDTR和GDT、GDTR startup_32:movl $0x10,%eaxmov %ax,%dsmov %ax,%esmov %ax,%fsmov %ax,%gslss _stack_start,%espcall setup_idtcall setup_gdtmovl $0x1…

33、单元测试实战练习题

以下是三个练习题的具体实现方案&#xff0c;包含完整代码示例和详细说明&#xff1a; 练习题1&#xff1a;TDD实现博客评论功能 步骤1&#xff1a;编写失败测试 # tests/test_blog.py import unittest from blog import BlogPost, Comment, InvalidCommentErrorclass TestBl…

16-算法打卡-哈希表-两个数组的交集-leetcode(349)-第十六天

1 题目地址 349. 两个数组的交集 - 力扣&#xff08;LeetCode&#xff09;349. 两个数组的交集 - 给定两个数组 nums1 和 nums2 &#xff0c;返回 它们的 交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。 示例 1&#xff1a;输入&#xff1a;nu…

SciPy库详解

SciPy 是一个用于数学、科学和工程计算的 Python 库&#xff0c;它建立在 NumPy 之上&#xff0c;提供了许多高效的算法和工具&#xff0c;用于解决各种科学计算问题。 CONTENT 1. 数值积分功能代码 2. 优化问题求解功能代码3. 线性代数运算功能代码 4. 信号处理功能代码 5. 插…

杰弗里·辛顿:深度学习教父

名人说&#xff1a;路漫漫其修远兮&#xff0c;吾将上下而求索。—— 屈原《离骚》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 杰弗里辛顿&#xff1a;当坚持遇见突破&#xff0c;AI迎来新纪元 一、人物简介 杰弗…

BladeX单点登录与若依框架集成实现

1. 概述 本文档详细介绍了将BladeX认证系统与若依(RuoYi)框架集成的完整实现过程。集成采用OAuth2.0授权码流程&#xff0c;使用户能够通过BladeX账号直接登录若依系统&#xff0c;实现无缝单点登录体验。 2. 系统架构 2.1 总体架构 #mermaid-svg-YxdmBwBtzGqZHMme {font-fa…

初识Redis · set和zset

目录 前言&#xff1a; set 基本命令 交集并集差集 内部编码和应用场景 zset 基本命令 交集并集差集 内部编码和应用场景 应用场景&#xff08;AI生成&#xff09; 排行榜系统 应用背景 设计思路 热榜系统 应用背景 设计思路 热度计算方式 总结对比表 前言&a…

playwright 教程高级篇:掌握网页自动化与验证码处理等关键技术详解

Playwright 教程高级篇:掌握网页自动化与验证码处理等关键技术详解 本教程将带您一步步学习如何使用 Playwright——一个强大的浏览器自动化工具,来完成网页任务,例如提交链接并处理旋转验证码。我们将按照典型的自动化流程顺序,从启动浏览器到关闭浏览器,详细讲解每个步骤…

数据结构(完)

树 二叉树 构建二叉树 int value;Node left;Node right;public Node(int val) {valueval;} 节点的添加 Node rootnull;public void insert(int num) {Node nodenew Node(num);if(rootnull) {rootnode;return;}Node index root;while(true) {//插入的节点值小if(index.value&g…

FastAPI与SQLAlchemy数据库集成与CRUD操作

title: FastAPI与SQLAlchemy数据库集成与CRUD操作 date: 2025/04/16 09:50:57 updated: 2025/04/16 09:50:57 author: cmdragon excerpt: FastAPI与SQLAlchemy集成基础包括环境准备、数据库连接配置和模型定义。CRUD操作通过数据访问层封装和路由层实现,确保线程安全和事务…

一个基于Django的写字楼管理系统实现方案

一个基于Django的写字楼管理系统实现方案 用户现在需要我用Django来编写一个写字楼管理系统的Web版本&#xff0c;要求包括增删改查写字楼的HTML页面&#xff0c;视频管理功能&#xff0c;本地化部署&#xff0c;以及人员权限管理&#xff0c;包含完整的代码结构和功能实现&am…

mongodb在window10中创建副本集的方法,以及node.js连接副本集的方法

创建Mongodb的副本集最好是新建一个文件夹&#xff0c;如D:/data&#xff0c;不要在mongodb安装文件夹里面创建副本集&#xff0c;虽然这样也可以&#xff0c;但是容易造成误操作或路径混乱&#xff1b;在新建文件夹里与现有 MongoDB 数据隔离&#xff0c;避免误操作影响原有数…

Maven 多仓库与镜像配置全攻略:从原理到企业级实践

Maven 多仓库与镜像配置全攻略&#xff1a;从原理到企业级实践 一、核心概念&#xff1a;Repository 与 Mirror 的本质差异 在 Maven 依赖管理体系中&#xff0c;repository与mirror是构建可靠依赖解析链的两大核心组件&#xff0c;其核心区别如下&#xff1a; 1. Repositor…

STM32 四足机器人常见问题汇总

文章不介绍具体参数&#xff0c;有需求可去网上搜索。 特别声明&#xff1a;不论年龄&#xff0c;不看学历。既然你对这个领域的东西感兴趣&#xff0c;就应该不断培养自己提出问题、思考问题、探索答案的能力。 提出问题&#xff1a;提出问题时&#xff0c;应说明是哪款产品&a…

MySQL 中 `${}` 和 `#{}` 占位符详解及面试高频考点

文章目录 一、概述二、#{} 和 ${} 的核心区别1. 底层机制代码示例 2. 核心区别总结 三、为什么表名只能用 ${}&#xff1f;1. 预编译机制的限制2. 动态表名的实现 四、安全性注意事项1. ${} 的风险场景2. 安全实践 五、面试高频考点1. 基础原理类问题**问题 1**&#xff1a;**问…

C语言编译预处理2

#include <XXXX.h>和#include <XXXX.c> #include "XXXX.h" 是 C 语言中一条预处理指令 #include <XXXX.h>&#xff1a;这种形式用于包含系统标准库的头文件。预处理器会在系统默认的头文件搜索路径中查找XXXX.h 文件。例如在 Linux 系统中&#…

Elasticvue-轻量级Elasticsearch可视化管理工具

Elasticvue一个免费且开源的 Elasticsearch 在线可视化客户端&#xff0c;用于管理 Elasticsearch 集群中的数据&#xff0c;完全支持 Elasticsearch 版本 8.x 和 7.x. 功能特色&#xff1a; 集群概览索引和别名管理分片管理搜索和编辑文档REST 查询快照和存储库管理支持国际…

Git提交规范及最佳实践

Git 提交规范通常是为了提高代码提交的可读性、可维护性和自动化效率&#xff08;如生成 ChangeLog&#xff09;。以下是常见的 Conventional Commits 规范&#xff0c;结合社区最佳实践总结而成&#xff1a; 1. 提交格式 每次提交的 commit message 应包含三部分&#xff1a;…

Ubuntu中snap

通过Snap可以安装众多的软件包。需要注意的是&#xff0c;snap是一种全新的软件包管理方式&#xff0c;它类似一个容器拥有一个应用程序所有的文件和库&#xff0c;各个应用程序之间完全独立。所以使用snap包的好处就是它解决了应用程序之间的依赖问题&#xff0c;使应用程序之…