当前位置: 首页 > news >正文

【C++】运算符重载

引言

内置类型可以直接用运算符,简单并且可以直接转为对应的指令。

而自定义类型不可以直接用,没有对应的指令。

例:比较int 简单,我们知道2>1,

而比较Date 对象,单纯用<>=符号,我们不知道该怎样进行比较。

所以,需要我们自行规定怎样去进行比较,这就是运算符重载。

实质

运算符重载的实质是具有特殊名字的函数

格式:返回类型 operator+运算符 (参数列表){函数体}

说明:

  • 同样具有返回类型(与运算符操作结果保持一致)、参数列表和函数体。
  • 参数个数和运算符作用的运算对象数量一样多。

例:比较Date对象。先将运算符重载函数定义在全局。

注意:

参数列表用 const 引用对象。避免操作失误导致对象被意外改变。避免权限被放大。


将运算符重载定义在全局,而对于全局函数,若要访问的成员变量被类 私有化,出现问题。

有以下几种解决方案:

  1. 成员放公有
  2. 提供Getxxx函数(Java常用)
  3. 友元函数
  4. 直接将该函数定义到类里面做成员函数。注意此时参数列表中隐含了 this 作为参数之一。

class Date { public: Date(int year = 1, int month =1, int day =1) { _year = year; _month = month; _day = day; } void Print() { cout << _year << '年' << _month << "月" << _day << "日" << endl; } //重载为成员函数,解决访问问题。 //这里只需要再传入一个参数即可,默认第一个参数为this bool operator == (const Date& d1) { return _year == d1._year && _month == d1._month && _day == d1._day; } private: int _year; int _month; int _day; };

调用方式

函数调用有两种形式。

Date d1(2026, 6, 13); Date d2(2026, 6, 13); //运算符重载的两种调用方式。 d1 == d2; //这种比较常用。 d1.operator==(d2); //注意优先级,这里<<优先级高于==,所以加() cout << (d1 == d2) << endl;

如何设计

要有意义。

例:

1.日期减天数、日期减日期,减号(-) 重载具有意义。

Date& Date::operator-=(int day) { if (day < 0) //用户可能输入负数。调用+=来实现。 { return *this += -day; } _day -= day; while (_day <= 0) { --_month; if (_month == 0) { --_year; _month = 12; } //先将月份借位了,再计算_day. _day += GetMonthDay(_year,_month); } return *this; } //但是,这种实现逻辑,结束后原日期也被更改。不是我们要的结果。 //要让原日期不跟着改变,用拷贝构造一个辅助日期来进行操作。 Date Date::operator-(int day)const { Date tmp(*this); //为了拿到当前的调用对象,拷贝构造传*this进去。 tmp -= day; // 调用-= 函数来复用。优化了代码。 return tmp; }
//两日期相减 *this-d=? int Date::operator-(const Date& d)const { Date max = *this; Date min = d; int flag = 1; if (*this < d) { max = d; min = *this; flag = -1; } int count = 0; while (max != min) //++代码复用,min每次+1,直到==max, { //加了多少次就差了多少天。 ++min; ++count; } return count * flag; }

2.日期相加无意义。但是日期加xx天,有意义。故+号可以重载。

Date& Date::operator+=(int day) { if (day < 0) //用户可能输入负数,调用-=来实现。 { return *this -= -day; } _day += day; while (_day > GetMonthDay(_year,_month)) { _day -= GetMonthDay(_year, _month); ++_month; //使用前置--,减少拷贝。 if (_month == 13) { _month = 1; ++_year; } } return *this; } Date Date::operator+(int day)const { Date tmp(*this); tmp += day; return tmp; }

实现逻辑:如何获取某一月的总天数。统一用GetMonthDay函数实现

(因为是频繁调用的小函数,考虑用内联

//Date类成员函数 //调用频繁且规模小,考虑设为内联函数,声明和定义不可分离。 int GetMonthDay(int year,int month) const { assert(month > 0 && month < 13); //设置为静态数组,不用每次调用都开辟新空间,创建一样的数组。 //用数组下标表示月份,数组对应的值表示该月的总天数。 static int monthDayArray[13] = { -1,31,28,31,30,31,30,31,31,30,31,30,31 }; //判断先判断简单的month==2,效率高一点 if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))) { return 29; } else { return monthDayArray[month]; } }

tips:能用引用返回就用引用返回,减少拷贝。判断函数结束后,返回的值还存不存在,存在就能引用返回。

注意

  1. 重载的运算符必须是语法中有的符号,不能自己创建新的。
  2. 重载操作符至少有一个类类型参数。不作用于完全的内置类型间。
    //编译报错:"operator +"必须至少有一个类类型的形参 int operator+(int x, int y) { return x - y; }
  3. (.* :: sizeof ?: . )这5个运算符不可重载。(*可以重载)

解释(.*) :了解即可

成员函数指针

成员函数要取地址,前面得加 & 取地址符号。

成员函数指针和成员函数在声明时也得指定类域。

成员函数指针调用,需要用对象进行调用,此时 (aa.*func)();

class A { public: void func() { cout << "A::func()" << endl; } }; typedef void(A::* PF)();//成员函数指针类型
//C++中规定成员函数要加&才能取到函数指针; PF pf = &A::func; A obj;//定义ob类对象temp //对象调用成员函数指针时,使用.*运算符 (obj.*pf)();

4. 函数重载和运算符重载,重载的意义不同。两个运算符重载可以构成函数重载。

例如:++有前置、后置之分。为了区分,规定对于后置++,参数列表增加一个int。

与前置构成重载,进行区分。但我们并不会真正使用int,只是让编译器能够区分出两种++,自动进行类型匹配 。

//类成员函数中定义。 //前置++,先++操作,再返回值 Date& operator++() { _day += 1; return *this; } //后置++,先返回值,再++操作 Date operator++(int) { Date tmp(*this); _day += 1; return tmp; }
Date d1(2026, 6, 13); Date d2(2026, 6, 13); Date d3 = ++d1; d1.Print(); d3.Print(); Date d4 = d2++; d2.Print(); d4.Print();

前置++性能更好,因为是引用返回,减少了拷贝,而后置需要进行2次拷贝。


赋值 运算符重载

同构造、析构、拷贝构造函数一样,也是一个默认的成员函数

与拷贝构造进行区分

(因为都要用到 = 号进行操作,容易混淆)

  • 相同点:它们都进行对象的拷贝。
  • 不同点:拷贝构造核心是用一个已存在的对象去初始化一个需要实例化的对象构造),

而赋值运算符重载是针对已经存在的两对象之间的拷贝。

特点

  1. 是一个运算符重载,但规定必须重载为成员函数参数建议写成const 引用,减少拷贝。
  2. 要设置返回值,才可以连续赋值若返回值还存在,建议用引用返回,减少拷贝
  3. 无显式实现时,编译器会自动生成。与拷贝构造一致,对内置类型成员变量完成值拷贝/浅拷贝(一个字节一个字节拷贝)对自定义类型成员变量,调用他的赋值运算符重载。(参考拷贝构造,深浅拷贝)显式析构、显式拷贝构造、显式赋值运算符重载,应该是同时出现的。
Date(const Date& d) //拷贝构造,参数建议写成const引用 { cout << "Date(const Date& d)" << endl; _year = d._day; _month = d._month; _day = d._day; } Date& operator=(const Date& d) { cout << "Date& operator=(const Date& d)" << endl; //用对象的地址检查:自己给自己赋值的情况。 if (this != &d) { _year = d._day; _month = d._month; _day = d._day; } //d1 = d2表达式的返回对象应该为d1,也就是*this return* this; }
Date d1(2026, 6, 13); Date d2(d1); //调用拷贝构造,将d1拷贝给d3 Date d3 = d1; Date d4(2026,6,14); d1 = d4; //调用赋值运算符重载


流插入/提取 运算符重载

是什么?

依托函数重载可以对不同类型操作打印和输入。

C中的print和scanf无论怎样设置,都无法对自定义类型操作,

C++提出流插入/提取重载,就可以对自定义类型进行操作

说明:cout是 ostream类型 的对象。cin是 istream类型 的对象。

ostream& operator<<(ostream& out, const Date& d) { out << d._year << "年" << d._month << "月" << d._day << "日" << endl; return out; }
istream& operator>>(istream& in, Date& d) { cout << "请依次输入年月日:>" << endl; in >> d._year >> d._month >> d._day; while(!d.CheckDate()) { cout << "请重新输入:" << endl; in >> d._year >> d._month >> d._day; } return in; }

注意

但凡重载为成员函数,就会自动把this置于第一个形参位置。即this在前,cout在后。

用 cout<<d ;调用时,因与形参的顺序不一致,出错。

所以,不做成员函数,而做全局函数。重置形参顺序。但是,无法访问到类中私有的成员变量。

解决:在类中声明为友元函数。全局函数声明和友元函数声明都得来一遍,因为它们的域不同。

cin也是一样的逻辑。

const成员函数

为什么要用?

//成员函数 void Print(); //编译出错,Print函数中第一个形参是Date* this //将const Date传入,导致权限放大。 void TestDate() { const Date d1(2026, 4, 14); d1.Print(); }

解决:使用const修饰成员函数,形参会被设置为const Date 权限平级了,可以使用。

格式

void Print() const;

建议

凡是不改变调用对象的函数都建议用const修饰。

//非const对象也可以调用const成员函数,权限可以缩小 Date d1(2026,6,14); d1.Print(); const Date d2(2026,4,5); d2.Print();

取地址运算符重载

分为:普通取地址运算符重载const取地址运算符重载。它们构成函数重载。

一般这两个函数编译器自动生成的已经够用 了,我们不需要显式实现。

class Date { public: Date* operator&() { return this; //return nullptr; } const Date* operator&() const { return this; //return nullptr; } private: int _year; int _month; int _day; };
http://www.cnnetsun.cn/news/2935943.html

相关文章:

  • 【Zephyr开发系列-7】Zephyr程序调试解析
  • 5分钟搞定音频字幕:Open-Lyrics智能转录翻译完整指南
  • QUICC Engine子系统:嵌入式通信硬件加速与多线程机制解析
  • 阿里JDK源码核心剖析:程序员进阶必备!
  • SK-H1-ASICBD-D1030控制器模块
  • java毕业设计下载(全套源码+配套论文)——基于java+原生Sevlet+socket的聊天室系统设计与实现
  • Agent Scope Java 2.x 系列【17】Harness:工作区远程存储模式
  • 移动端工程师进阶:AI原生App,月薪20K到35K的秘密
  • RTD2166-CG,内置 MCU 实现 DP-VGA 无缝转换
  • GTA5线上小助手:完全免费的洛圣都游戏增强神器终极指南
  • 3步解锁B站大会员4K视频下载:专业工具全攻略
  • 2026 最新 PS 抠图白边彻底消除教程(无痕无损)
  • 如何轻松下载B站4K高清视频:3分钟搞定会员专属内容
  • MPC866通信处理器SDMA/IDMA与串行接口(TSA)配置详解
  • 别再乱用`torch.cat`和`torch.stack`了!详解张量拼接与维度对齐的常见坑(附解决方案)
  • 告别盲目调参!手把手教你用ENVI官方插件和脚本,高效玩转遥感影像深度学习
  • 深度解析:douyin-live-go如何构建高性能抖音直播数据采集系统
  • 终极Office激活方案:Ohook免费解锁Microsoft 365完整功能指南
  • QRazyBox:让损坏的二维码重获新生的专业修复工具
  • 三步免费解锁Wand专业版:开源增强工具完整使用指南
  • 【Springboot毕设全套源码+文档】基于springboot+vue的民宿信息管理系统(丰富项目+远程调试+讲解+定制)
  • 团队编程协作方案:从代码到Wiki的高效落地实践
  • PXD10 QuadSPI接口深度解析:双模式设计、内存映射与低功耗实战
  • 嵌入式系统性能剖析:从硬件计数器到跟踪缓冲器的实战指南
  • 嵌入式工程师必看:手把手教你排查PHY芯片挂载失败(从供电到MDIO波形全流程)
  • PXS20微控制器ADC自测试与时钟配置:功能安全与高可靠信号采集实战
  • 计算机毕业设计之java-微信小程序的律师事务所服务平台
  • LLM 应用的成本优化策略:从 Token 精简到模型分层的实战路径
  • 2026年AI写作辅助平台对比实测:5款神器从构思到提交全流程护航
  • ExDark:破解低光照计算机视觉难题的7363张图像数据集解决方案