C++面向对象整理(7)之运算符重载、operator关键字

C++面向对象整理(7)之运算符重载、operator关键字

注:整理一些突然学到的C++知识,随时mark一下
例如:忘记的关键字用法,新关键字,新数据结构


C++ 的 类的运算符重载

  • C++面向对象整理(7)之运算符重载、operator关键字
  • 一、运算符重载
    • 1、运算符重载的定义
    • 2、加号的重载
    • 3、赋值号的重载
  • 二、动态分配的内存时赋值`=`的运算符重载
  • 总结可以重载的运算符


提示:本文为 C++ 中 运算符重载 的写法和举例


一、运算符重载

1、运算符重载的定义

  运算符重载允许程序员为自定义数据类型(如类)重新定义或重载运算符的行为。通过这种方式,我们可以使自定义类型的对象像内置类型(如int、float等)一样使用运算符。

当我们定义了一个类,并希望这个类的对象能够使用某个运算符时,我们就可以在那个类中重载那个运算符。重载运算符本质上就是定义一个特殊的成员函数或友元函数,这个函数的名字就是运算符的符号。
运算符重载的格式基本上遵循了函数的定义格式,只不过函数名被替换成了operator后跟要重载的运算符。

ReturnType operator 运算符号(参数){ }

下面,我将以加号+和赋值号=为例,说明如何在C++中重载运算符。

2、加号的重载

加号+的重载
假设我们有一个简单的复数类Complex,我们想要重载加号运算符来让两个复数相加。

class Complex {  
public:  double real;  double imag;  // 构造函数  Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}  // 加号运算符重载,作为成员函数  Complex operator+(const Complex& rhs) const {  return Complex(real + rhs.real, imag + rhs.imag);  }  // 其他成员函数,如输出等...  
};  int main() {  Complex c1(1, 2);  Complex c2(3, 4);  Complex sum = c1 + c2; // 使用重载的+运算符  // ...  
}

在这个例子中,operator+是一个成员函数,它接受一个类型为const Complex&的右操作数rhs。函数返回一个新的Complex对象,其实部和虚部分别是两个操作数的对应部分之和。

也可以将加号运算符+重载为非成员函数(并且作为友元),你需要修改Complex类,将operator+声明为友元,并在类外部定义它。以下是修改后的代码:

class Complex {  
public:  double real;  double imag;  // 构造函数  Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}  // 输出成员函数  void print() const {  std::cout << "(" << real << ", " << imag << ")" << std::endl;  }  // 声明+运算符重载为非成员函数且为友元  friend Complex operator+(const Complex& lhs, const Complex& rhs);  
};  // 非成员函数形式的+运算符重载,作为Complex类的友元  
Complex operator+(const Complex& lhs, const Complex& rhs) {  return Complex(lhs.real + rhs.real, lhs.imag + rhs.imag);  
}  int main() {  Complex c1(1, 2);  Complex c2(3, 4);  Complex sum = c1 + c2; // 使用重载的+运算符  sum.print(); // 输出结果  // ...  return 0;  
}

在本例中,operator+现在是一个非成员函数,它接受两个Complex类型的常量引用参数lhs和rhs,并返回一个新的Complex对象,该对象的实部和虚部分别是两个输入对象的实部和虚部之和。

由于operator+需要访问Complex类的私有成员real和imag,我们将其声明为Complex类的友元。这样,operator+就能够直接访问类的私有成员,而不需要通过类的公共接口。

在main函数中,我们可以像之前一样使用+运算符来相加两个Complex对象,并打印出结果。

3、赋值号的重载

赋值号=的重载,注意不写这个的话,编译器也会默认为一个类添加一个赋值运算符 operator= 的重载函数,这被称为合成赋值运算符(synthesized assignment operator)。这个合成的赋值运算符会逐个成员地将右侧对象的成员值赋给左侧对象的对应成员。下面给出对于赋值运算符=的重载的例子 :

class Complex {  
public:  double real;  double imag;  // ...其他成员函数...  // 赋值运算符重载  Complex& operator=(const Complex& rhs) {  if (this != &rhs) { // 检查自赋值  real = rhs.real;  imag = rhs.imag;  }  return *this; // 返回*this以支持链式赋值  }  
};  int main() {  Complex c1(1, 2);  Complex c2(3, 4);  c1 = c2; // 使用重载的=运算符  // ...  
}

在这个例子中,operator=返回对当前对象的引用(Complex&),这使得我们可以进行链式赋值,例如c1 = c2 = c3;。同时,我们在函数体内首先检查自赋值的情况,以避免不必要的操作,并防止潜在的错误。

重载运算符时需要注意以下几点:

不是所有运算符都可以被重载。例如,.、.*、::、sizeof、?:运算符不能被重载
重载的运算符必须至少有一个操作数是用户自定义类型
重载运算符不能改变运算符的优先级和结合性。
重载运算符通常应该保持其原有的语义,避免引起混淆。
对于成员函数形式的重载,左操作数总是类的实例(*this),而右操作数作为参数传递。
对于非成员函数形式的重载(通常作为友元函数),可以自由地定义左右操作数。
通过合理地重载运算符,我们可以使自定义类型的使用更加直观和方便。

二、动态分配的内存时赋值=的运算符重载

如果类中有动态分配的内存(即使用 new 在堆上创建的对象),并且没有正确地管理这些动态内存(例如,在赋值运算符或析构函数中释放它们),那么使用编译器提供的默认赋值运算符可能会导致问题。

当两个对象它们都拥有指向相同堆内存区域的指针,那么析构函数可能会多次释放同一块内存,导致未定义行为(通常是程序崩溃)。如果在赋值操作中没有正确地复制堆内存(即只复制了指针而没有复制所指向的内容),那么当原始对象被销毁并释放其内存时,新对象将包含一个指向已经被释放的内存的悬垂指针(dangling pointer)。
为了避免这些问题,需要为包含动态分配内存的类提供自定义的赋值运算符。这个自定义的赋值运算符应该执行深拷贝(deep copy),即要复制指针所指向的内容而不是复制一个指针。

下面是一个简单的例子,展示了如何为包含动态分配内存的类提供自定义赋值运算符:

class MyClass {  
public:  MyClass(int size) {  data = new int[size];  dataSize = size;  }  ~MyClass() {  delete[] data;  }  MyClass& operator=(const MyClass& other) {  if (this != &other) { // 检查自赋值  delete[] data; // 释放当前对象的内存  dataSize = other.dataSize;  data = new int[dataSize]; // 分配新内存  std::copy(other.data, other.data + dataSize, data); // 复制数据  }  return *this; // 返回当前对象的引用以支持链式赋值  }  private:  int* data;  int dataSize;  
};

在这个例子中,MyClass 的赋值运算符首先检查是否发生了自赋值,然后释放当前对象的内存,并根据 other 对象的大小分配新的内存。最后,它使用 std::copy 来复制数据。这样,每次赋值时都会创建新的堆内存区域,避免了重复释放和悬垂指针的问题。

总结可以重载的运算符

可以重载的运算符相当丰富,包括一元运算符、二元运算符以及某些特殊运算符。以下是一些常用的可重载的运算符:

一元运算符:
+(正号)
-(负号)
*(指针解引用)
&(取地址)
~(按位取反)
!(逻辑非)
++(自增)
--(自减)
->(指向成员的指针)
newdelete(尽管它们不是真正的运算符,但它们的行为可以通过重载操作符函数来定制)二元运算符:
+(加法)
-(减法)
*(乘法)
/(除法)
%(取模)
+=(加等于)
-=(减等于)
*=(乘等于)
/=(除等于)
%=(模等于)
<<(左移)
>>(右移)
<<=(左移等于)
>>=(右移等于)
==(等于)
!=(不等于)
>(大于)
<(小于)
>=(大于等于)
<=(小于等于)
&(按位与)
|(按位或)
^(按位异或)
&&(逻辑与)
||(逻辑或)
=(赋值)
->*(成员访问)
,(逗号)特殊运算符:
()(函数调用)
[](下标访问)
new[]delete[](数组形式的newdeleteoperator 关键字后的类型转换函数,例如 operator int()

需要注意的是,不是所有的运算符都可以被重载。C++标准规定了几种运算符不能重载,包括:

.(成员访问)
.*(成员指针访问)
::(作用域解析)
sizeof(获取对象或类型大小)
? :(条件运算符,即三目运算符)

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

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

相关文章

力扣3. 无重复字符的最长子串

Problem: 3. 无重复字符的最长子串 文章目录 题目描述思路及解法复杂度Code 题目描述 思路及解法 1.川建一个set集合存储最长的无重复的字符&#xff1b; 2.创建双指针p、q&#xff0c;每次当q指针指向的字符不在set集合中时将其添加到set集合中让q指针后移&#xff0c;并且更新…

【算法每日一练]

目录 今日知识点&#xff1a; 辗转相减法化下三角求行列式 组合数动态规划打表 约数个数等于质因数的次方1的乘积 求一个模数 将n个不同的球放入r个不同的盒子&#xff1a;f[i][j]f[i-1][j-1]f[i-1][j]*j 将n个不同的球放入r个相同的盒子&#xff1a;a[i][j]a[i-j][j]a[…

高架学习笔记之需求工程

目录 一、什么是软件需求 二、需求工程 2.1. 需求获取 2.2. 需求分析 2.3. 形成需求规格 2.4. 需求确认 2.5. 需求管理 2.5.1. 变更控制 2.5.2. 版本控制 2.5.3. 需求跟踪 2.5.4. 需求状态跟踪 一、什么是软件需求 软件需求目前没有统一的定义&#xff0c;一般是指用…

Vue3:直接对一个响应式对象数据进行结构复制会丢失响应式效果

一、问题描述 我们在进行路由页面传参的时候&#xff0c;使用query方式传递数据 这个时候&#xff0c;接收数据的组件&#xff0c;会从useRoute的query属性里面获取数据 如果&#xff0c;这里使用结构赋值语法&#xff0c;那么&#xff0c;获取到的数据&#xff0c;会失去响应…

手写一个LRU

import java.util.LinkedHashMap; import java.util.Map;public class LRUCache<K, V> extends LinkedHashMap<K, V> {private final int cacheSize;public LRUCache(int cacheSize) {// 设置访问顺序为访问顺序&#xff0c;即最近访问的元素将被放置在队列尾部sup…

从零开始学HCIA之网络基础知识01

1、20世纪70年代末&#xff0c;为了打破不同厂商设备之间无法相互通信的界限&#xff0c;ISO&#xff08;International Organization for Standardization&#xff0c;国际标准化组织&#xff09;开发了OSI参考模型&#xff08;Open System Interconnection Reference Model&a…

树的遍历方式DFS和BFS

DFS(depth first search) 深度优先遍历 从图中一个未访问的顶点V开始&#xff0c;沿着一条路一直走到底&#xff0c;然后从这条路尽头的节点回退到上一个节点&#xff0c;再从另一条路走到底…不断递归重复这个过程&#xff0c;直到所有的顶点都遍历完成。前序遍历&#xff0c…

前端框架前置课(1)---AJAX阶段

1. AJAX入门 1.1 AJAX概念和axios使用 1.1.1 什么是AJAX? 1.1.2 怎么用AJAX? 引入axios.js 获取省份列表数据 1.2 认识URL 1.3 URL查询参数 1.4 常用请求方和数据提交 1.5 HTTP协议-报文 1.5.1 HTTP响应状态码 1.5.1.1 状态码&#xff1a;1XX&#xff08;信息&#xff09…

字母在字符串中的百分比

字母在字符串中的百分比 链接:https://leetcode.cn/problems/percentage-of-letter-in-string/description/ 给你⼀个字符串 s 和⼀个字符 letter &#xff0c;返回在 s 中等于 letter 字符所占的 百分比 &#xff0c;向下取整到最接近的百分比。 输⼊&#xff1a;s “foob…

sox命令用法

play input.wav 播放音频 sox input.wav -n stat 查看音频文件信息 soxi input.wav 查看音频文件信息input sox input.wav -n stat -v 不失真最大调整量 sox -v 0.8 input.wav output.wav 调整音量0.8,&#xff08;大于1为扩大&#xff0c;小于1为缩小&#xff09; sox *.wav *…

用最小堆实现通用的高效定时器组件

用最小堆实现通用的高效定时器组件 文章目录 用最小堆实现通用的高效定时器组件开篇解决方案类图源码实现测试总结 开篇 在程序开发过程中&#xff0c;定时器会经常被使用到。而在Linux应用开发中&#xff0c;系统定时器资源有限&#xff0c;进程可创建的定时器数量会受到系统限…

力扣爆刷第103天之CodeTop100五连刷1-5

力扣爆刷第103天之CodeTop100五连刷1-5 文章目录 力扣爆刷第103天之CodeTop100五连刷1-5一、3. 无重复字符的最长子串二、206. 反转链表三、146. LRU 缓存四、215. 数组中的第K个最大元素五、25. K 个一组翻转链表 一、3. 无重复字符的最长子串 题目链接&#xff1a;https://l…

计算机软件安全

一、软件安全涉及的范围 1.1软件本身的安全保密 软件的本质与特征&#xff1a; 可移植性 寄生性 再生性 可激发性 攻击性 破坏性 …… 知识产权与软件盗版 软件商品交易形式不透明&#xff0c;方式多样&#xff0c;传统商标标识方法不适用&#xff1b; 盗版方法简捷…

SFML udp通信实例

包含的lib库文件&#xff0c;免得一个一个复制名称&#xff1a; sfml-window-d.lib sfml-system-d.lib sfml-audio-d.lib sfml-graphics-d.lib sfml-main-d.lib sfml-network-d.lib vorbis.lib vorbisenc.lib vorbisfile.lib void runUdpClient(unsigned short port) { /…

【Nginx】配置Nginx以支持主域名和二级域名

生命就像是一场告别 从起点对一切说再见 你拥有的仅仅是伤痕 在回望来路的时候 那天我们相遇在街上 彼此寒暄并报以微笑 我们相互拥抱挥手道别 转过身后已泪流满面 &#x1f3b5; 蔡健雅《当我想你的时候》 Nginx是一款高性能的Web服务器和反向代理服务器…

springboot/ssm本科生交流培养管理平台Java高校课程选课系统web

springboot/ssm本科生交流培养管理平台Java高校课程选课系统web 基于springboot(可改ssm)vue项目 开发语言&#xff1a;Java 框架&#xff1a;springboot/可改ssm vue JDK版本&#xff1a;JDK1.8&#xff08;或11&#xff09; 服务器&#xff1a;tomcat 数据库&#xff1…

我的风采——android studio

目录 实现“我的风采”页面要求理论代码生成apk文件 实现“我的风采”页面 要求 要求利用’java框架的边框布局实现“找的风采 ”页而&#xff0c;其中中间为你的生活照&#xff0c;左右和下面为按钮&#xff0c;上面为标签 理论 Java GUI编程是Java程序设计的重要组成部分…

浩哥带你做项目,纯免费教学

浩哥带你做项目 一、YiYi-Web项目开发1. 简介2. 技术栈2.1 后端开发环境2.2 前端开发环境 3.项目截图 二、计算机游戏程序设计&#xff08;基础篇&#xff09;三、RuoYi-Cloud项目学习1.功能介绍2.项目截图 四、鸿蒙应用开发五、软考六、Linux基础知识学习 最近浩哥社区群涌进大…

项目1-加法计算器

1.创建项目 2.导入前端代码 2.1 static包内 2.2 测试前端代码是否有误 显示成功说明无误 2.3 定义用户接口 请求路径&#xff1a;calc/sum 请求方式&#xff1a;GET/POST 接口描述&#xff1a;计算两个整数相加 请求参数: 参数名类型是否必须备注num1Integer是参与计算的第…

JAVA 100道题(16)

16.编写一个程序&#xff0c;尝试打开一个不存在的文件&#xff0c;并捕获可能抛出的异常。 在Java中&#xff0c;你可以使用try-catch块来捕获和处理异常。下面是一个示例程序&#xff0c;尝试打开一个不存在的文件&#xff0c;并捕获FileNotFoundException异常&#xff1a; j…