C++ 运算符重载:自定义类型的运算扩展

C++ 运算符重载:自定义类型的运算扩展

C++ 运算符重载:自定义类型的运算扩展

在这里插入图片描述

💡 学习目标:掌握运算符重载的核心语法与规则,能够为自定义类型重载常用运算符,实现类对象的灵活运算。
💡 学习重点:运算符重载的基本形式、成员函数与全局函数重载的区别、常见运算符的重载实现、禁止重载的运算符。

一、运算符重载的概念与核心价值

结论:运算符重载是 C++ 静态多态的重要体现,允许为自定义类型(如类、结构体)重新定义运算符的行为,让自定义对象可以像内置类型一样使用运算符。

运算符重载的核心价值:

  1. 简化代码书写:用直观的运算符替代繁琐的成员函数调用,提升代码可读性
  2. 统一操作风格:让自定义类型的运算逻辑与内置类型保持一致,降低学习和使用成本
  3. 扩展类型功能:根据业务需求定制运算符的行为,满足自定义类型的运算需求

⚠️ 注意事项:运算符重载不会改变运算符的优先级和结合性,也不会改变运算符的操作数个数。

二、运算符重载的基本语法

运算符重载的本质是函数重载,分为成员函数重载全局函数重载两种形式。

2.1 成员函数重载语法

将运算符重载函数定义为类的成员函数,语法格式如下:

class 类名 {public: 返回值类型 operator运算符(参数列表){// 自定义运算逻辑}};
  • 一元运算符(如 ++--)作为成员函数时,没有参数
  • 二元运算符(如 +-)作为成员函数时,只有一个参数,表示运算符右侧的操作数

2.2 全局函数重载语法

将运算符重载函数定义为全局函数,语法格式如下:

返回值类型 operator运算符(参数列表){// 自定义运算逻辑}
  • 一元运算符作为全局函数时,有一个参数
  • 二元运算符作为全局函数时,有两个参数,分别表示左右两侧的操作数
  • 如果需要访问类的私有成员,可将全局重载函数声明为类的友元函数

三、常见运算符的重载实现

3.1 二元运算符重载:以 + 为例

以自定义 Point 类为例,重载 + 运算符实现两个点的坐标相加。

3.1.1 成员函数重载 + 运算符
#include<iostream>usingnamespace std;classPoint{public:int x, y;// 构造函数Point(int x =0,int y =0):x(x),y(y){}// 成员函数重载 + 运算符 Point operator+(const Point& p){returnPoint(this->x + p.x,this->y + p.y);}// 打印点坐标voidprint(){ cout <<"("<< x <<", "<< y <<")"<< endl;}};intmain(){ Point p1(1,2),p2(3,4); Point p3 = p1 + p2;// 等价于 p1.operator+(p2) p3.print();// 输出 (4, 6)return0;}
3.1.2 全局函数重载 + 运算符
#include<iostream>usingnamespace std;classPoint{public:int x, y;// 构造函数Point(int x =0,int y =0):x(x),y(y){}// 打印点坐标voidprint(){ cout <<"("<< x <<", "<< y <<")"<< endl;}// 声明友元函数friend Point operator+(const Point& p1,const Point& p2);};// 全局函数重载 + 运算符 Point operator+(const Point& p1,const Point& p2){returnPoint(p1.x + p2.x, p1.y + p2.y);}intmain(){ Point p1(1,2),p2(3,4); Point p3 = p1 + p2;// 等价于 operator+(p1, p2) p3.print();// 输出 (4, 6)return0;}

3.2 一元运算符重载:以 ++ 为例

一元运算符分为前置++和后置++,二者的重载方式有区别。

3.2.1 前置 ++ 运算符重载

前置 ++ 表示先自增,再使用,重载时没有参数

#include<iostream>usingnamespace std;classCounter{private:int count;public:Counter(int c =0):count(c){}// 成员函数重载前置 ++ Counter&operator++(){this->count++;return*this;// 返回自增后的对象,支持链式操作}voidshow(){ cout <<"计数:"<< count << endl;}};intmain(){ Counter c(5);++c;// 等价于 c.operator++() c.show();// 输出 计数:6 Counter c2 =++c; c2.show();// 输出 计数:7return0;}
3.2.2 后置 ++ 运算符重载

后置 ++ 表示先使用,再自增,重载时需要添加一个占位参数 int 区分前置版本。

#include<iostream>usingnamespace std;classCounter{private:int count;public:Counter(int c =0):count(c){}// 成员函数重载后置 ++ Counter operator++(int){ Counter temp =*this;// 保存当前对象状态this->count++;return temp;// 返回自增前的对象}voidshow(){ cout <<"计数:"<< count << endl;}};intmain(){ Counter c(5); Counter c2 = c++;// 等价于 c.operator++(0) c.show();// 输出 计数:6 c2.show();// 输出 计数:5return0;}

3.3 关系运算符重载:以 == 为例

重载关系运算符(==!=<> 等),实现自定义对象的比较逻辑。

#include<iostream>#include<string>usingnamespace std;classStudent{private: string name;int id;public:Student(string name,int id):name(name),id(id){}// 成员函数重载 == 运算符booloperator==(const Student& s){returnthis->id == s.id;// 按学号判断是否为同一学生} string getName(){return name;}};intmain(){ Student s1("张三",2024001); Student s2("李四",2024002); Student s3("张三",2024001);if(s1 == s3){ cout << s1.getName()<<" 和 "<< s3.getName()<<" 是同一学生"<< endl;}else{ cout <<"不是同一学生"<< endl;}if(s1 == s2){ cout <<"是同一学生"<< endl;}else{ cout << s1.getName()<<" 和 "<< s2.getName()<<" 不是同一学生"<< endl;}return0;}

3.4 输入输出运算符重载:<<>>

<<>> 运算符通常需要全局函数 + 友元的方式重载,因为左侧操作数是 ostreamistream 对象。

#include<iostream>#include<string>usingnamespace std;classPerson{private: string name;int age;public:Person(string name ="",int age =0):name(name),age(age){}// 声明友元函数,重载 << 运算符friend ostream&operator<<(ostream& os,const Person& p);// 声明友元函数,重载 >> 运算符friend istream&operator>>(istream& is, Person& p);};// 重载 << 运算符,用于输出对象 ostream&operator<<(ostream& os,const Person& p){ os <<"姓名:"<< p.name <<",年龄:"<< p.age;return os;// 返回 os,支持链式输出}// 重载 >> 运算符,用于输入对象 istream&operator>>(istream& is, Person& p){ is >> p.name >> p.age;return is;// 返回 is,支持链式输入}intmain(){ Person p; cout <<"请输入姓名和年龄:"<< endl; cin >> p;// 等价于 operator>>(cin, p) cout <<"你输入的信息:"<< p << endl;// 等价于 operator<<(cout, p)return0;}

四、运算符重载的限制条件

4.1 禁止重载的运算符

以下运算符不允许重载,使用时需注意:

  • 成员访问运算符:.
  • 成员指针访问运算符:.*
  • 作用域解析运算符:::
  • 条件运算符:?:
  • 预处理运算符:#

4.2 必须以成员函数重载的运算符

以下运算符只能通过类的成员函数重载,不能使用全局函数:

  • 赋值运算符:=
  • 函数调用运算符:()
  • 下标运算符:[]
  • 箭头运算符:->

4.3 重载赋值运算符 = 的注意事项

赋值运算符 = 是类的默认成员函数,编译器会自动生成一个浅拷贝版本。当类中包含指针成员时,必须手动重载 = 实现深拷贝,避免浅拷贝导致的内存泄漏。

#include<iostream>#include<cstring>usingnamespace std;classString{private:char*str;public:// 构造函数String(constchar*s =""){ str =newchar[strlen(s)+1];strcpy(str, s);}// 析构函数~String(){delete[] str;}// 重载赋值运算符 =,实现深拷贝 String&operator=(const String& s){if(this==&s){// 防止自赋值return*this;}// 释放当前对象的内存delete[] str;// 分配新内存并拷贝数据 str =newchar[strlen(s.str)+1];strcpy(str, s.str);return*this;// 支持链式赋值}voidshow(){ cout << str << endl;}};intmain(){ String s1("Hello C++"); String s2; s2 = s1;// 调用重载的 = 运算符 s2.show();// 输出 Hello C++return0;}

⚠️ 核心注意点:重载赋值运算符时,必须返回 *this 的引用,否则无法支持链式赋值(如 a = b = c)。

五、实战案例:重载运算符实现复数运算

💡 需求:设计一个 Complex 类表示复数,重载 +-*<< 运算符,实现复数的加减乘运算和输出功能。

5.1 需求分析

  1. 复数的形式为 a+bia + bia+bi,包含实部 real 和虚部 imag 两个成员变量
  2. 重载 +:(a+bi)+(c+di)=(a+c)+(b+d)i(a+bi)+(c+di) = (a+c)+(b+d)i(a+bi)+(c+di)=(a+c)+(b+d)i
  3. 重载 -:(a+bi)−(c+di)=(a−c)+(b−d)i(a+bi)-(c+di) = (a-c)+(b-d)i(a+bi)−(c+di)=(a−c)+(b−d)i
  4. 重载 *:(a+bi)∗(c+di)=(ac−bd)+(ad+bc)i(a+bi)*(c+di) = (ac-bd)+(ad+bc)i(a+bi)∗(c+di)=(ac−bd)+(ad+bc)i
  5. 重载 <<:输出复数的标准格式

5.2 完整代码实现

#include<iostream>usingnamespace std;classComplex{private:double real;// 实部double imag;// 虚部public:// 构造函数Complex(double real =0,double imag =0):real(real),imag(imag){}// 成员函数重载 + 运算符 Complex operator+(const Complex& c){returnComplex(this->real + c.real,this->imag + c.imag);}// 成员函数重载 - 运算符 Complex operator-(const Complex& c){returnComplex(this->real - c.real,this->imag - c.imag);}// 成员函数重载 * 运算符 Complex operator*(const Complex& c){double r =this->real * c.real -this->imag * c.imag;double i =this->real * c.imag +this->imag * c.real;returnComplex(r, i);}// 友元函数重载 << 运算符friend ostream&operator<<(ostream& os,const Complex& c);};// 实现 << 运算符重载 ostream&operator<<(ostream& os,const Complex& c){if(c.imag >=0){ os << c.real <<" + "<< c.imag <<"i";}else{ os << c.real <<" - "<<-c.imag <<"i";}return os;}intmain(){ Complex c1(3,4),c2(1,-2); Complex c3 = c1 + c2; Complex c4 = c1 - c2; Complex c5 = c1 * c2; cout <<"c1 = "<< c1 << endl; cout <<"c2 = "<< c2 << endl; cout <<"c1 + c2 = "<< c3 << endl; cout <<"c1 - c2 = "<< c4 << endl; cout <<"c1 * c2 = "<< c5 << endl;return0;}

5.3 运行结果

c1 = 3 + 4i c2 = 1 - 2i c1 + c2 = 4 + 2i c1 - c2 = 2 + 6i c1 * c2 = 11 - 2i 

六、运算符重载的开发规范

  1. 保持语义一致:重载后的运算符行为应与内置类型的语义相近,避免让使用者产生困惑
  2. 优先使用成员函数:对于一元运算符和复合赋值运算符(如 +=-=),优先使用成员函数重载
  3. 输入输出用全局友元<<>> 运算符必须使用全局函数 + 友元的方式重载
  4. 深拷贝处理:当类包含指针成员时,必须手动重载赋值运算符 = 实现深拷贝
  5. 避免过度重载:只重载实际需要的运算符,不要为了重载而重载,以免增加代码复杂度

七、本章总结

✅ 运算符重载的本质是函数重载,分为成员函数和全局函数两种实现方式。
✅ 运算符重载不会改变运算符的优先级、结合性和操作数个数。
✅ 部分运算符有固定的重载方式,如 = 必须用成员函数,<< 必须用全局友元函数。
✅ 合理使用运算符重载可以简化代码、统一风格,是 C++ 面向对象编程的重要技能。

Read more

Flutter for OpenHarmony:string_validator 验证与清洗字符串,防御恶意输入(字符串校验工具箱) 深度解析与鸿蒙适配指南

Flutter for OpenHarmony:string_validator 验证与清洗字符串,防御恶意输入(字符串校验工具箱) 深度解析与鸿蒙适配指南

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 在处理用户输入时,“永远不要相信用户提交的数据” 是安全的第一原则。无论是注册表单的邮箱校验,还是 API 参数的格式清洗,字符串验证都是必不可少的环节。 string_validator 是一个功能极其丰富的字符串验证和清洗库,它移植自著名的 Node.js 库 validator.js。即使是复杂的信用卡号、ISBN、UUID 校验,它也能一行代码搞定。本文将介绍如何在 OpenHarmony 上利用它构建健壮的数据校验层。 一、核心功能 1.1 验证 (Validators) 返回 bool 值,判断字符串是否符合某种格式。 * isEmail: 邮箱格式 * isURL: 网址校验 * isCreditCard: 信用卡号(Luhn 算法) * isUUID:

By Ne0inhk
Flutter 三方库 http_helper 的鸿蒙化适配指南 - 打造标准化的 REST 客户端封装、支持响应式异常拦截与请求全流程钩子

Flutter 三方库 http_helper 的鸿蒙化适配指南 - 打造标准化的 REST 客户端封装、支持响应式异常拦截与请求全流程钩子

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 http_helper 的鸿蒙化适配指南 - 打造标准化的 REST 客户端封装、支持响应式异常拦截与请求全流程钩子 前言 在 Flutter for OpenHarmony 的网络层开发中,直接使用底层的 http 库往往会导致大量的模板代码,且在处理拦截器、错误码统一转换和 Loading 态管理时力不从心。http_helper 是一套轻量级但功能完备的 REST 客户端封装库。它能帮助鸿蒙开发者快速构建一套符合工程化标准的服务层代码。本文将指导大家如何利用该库提升鸿蒙应用的网络交互质量。 一、原理解析 / 概念介绍 1.1 基础原理 http_helper 基于 Dart 的 http 包进行二次封装。它通过引入 Interceptor、

By Ne0inhk
Flutter 三方库 serial_csv 的鸿蒙化适配指南 - 实现极速的流式 CSV 数据编解码、支持端侧超大规模表格数据的高效序列化实战

Flutter 三方库 serial_csv 的鸿蒙化适配指南 - 实现极速的流式 CSV 数据编解码、支持端侧超大规模表格数据的高效序列化实战

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 serial_csv 的鸿蒙化适配指南 - 实现极速的流式 CSV 数据编解码、支持端侧超大规模表格数据的高效序列化实战 前言 在进行 Flutter for OpenHarmony 的金融报表、工业数据采集或大型列表导出应用开发时,CSV(Comma-Separated Values)由于其通用的文本属性成为首选的数据交换格式。然而,当文件达到数万行甚至更庞大时,常规的字符串拼接会导致内存爆炸。serial_csv 是一款专为极致性能设计的流式解析库。本文将探讨如何在鸿蒙端构建稳健、低开销的大数据处理方案。 一、原原理性解析 / 概念介绍 1.1 基础原理 serial_csv 采用了一种“增量扫描(Incremental Scanning)”算法。在读取时,它不一次性将整个文件加载进内存,而是通过缓冲区轮询,

By Ne0inhk
Flutter for OpenHarmony:debounce_throttle 防抖与节流的艺术(优化用户交互与网络请求) 深度解析与鸿蒙适配指南

Flutter for OpenHarmony:debounce_throttle 防抖与节流的艺术(优化用户交互与网络请求) 深度解析与鸿蒙适配指南

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 在 UI 交互开发中,有两种极其常见的性能问题: 1. 用户狂点按钮:导致发起了 10 次重复的表单提交请求。 2. 搜索框实时搜索:用户每输入一个字母就触发一次 API 搜索,浪费流量且导致界面闪烁。 解决这两个问题的标准答案是:防抖 (Debounce) 和 节流 (Throttle)。 虽然我们可以自己写 Timer 来实现,但处理 Timer 的销毁、重启逻辑很容易出错(造成内存泄漏或 NPE)。 debounce_throttle 库提供了封装良好、线程安全的防抖节流工具类,专为 Dart/Flutter 设计。 对于 OpenHarmony 应用,合理使用这些策略能显著降低 CPU 占用,

By Ne0inhk