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

Obsidian接入AI完整配置指南

Obsidian接入AI完整配置指南

🚀 Obsidian接入AI完整配置指南 📋 目录导航 * 核心工具介绍 * 免费接入方案 * 详细配置步骤 * 模型选择建议 * 实用功能配置 * 使用场景示例 * 常见问题解决 * 高级技巧 🎯 核心工具:Copilot插件 Copilot是Obsidian中最强大的AI助手插件,让你的笔记体验智能化升级! 安装步骤(3步搞定) 1. 打开设置:Obsidian → 设置 → 社区插件 2. 搜索安装:搜索"Copilot"并点击安装 3. 启用重启:启用插件并重启Obsidian 💰 免费AI接入方案:OpenRouter OpenRouter提供多个免费AI模型,是性价比最高的选择! 第一步:注册账号 * 🌐 访问:https://openrouter.ai/ * 🔗 使用GitHub或Google快速注册 * 🔑 在Keys页面生成API密钥 第二步:添加模型配置 模型添加方法: 1. ✅ 模型名称必须正确 2. 🌐 URL同图片地址

By Ne0inhk
AI数据标注平台的选型与实践:效率提升背后的技术逻辑

AI数据标注平台的选型与实践:效率提升背后的技术逻辑

👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕人工智能这个话题展开,希望能为你带来一些启发或实用的参考。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获! 文章目录 * AI数据标注平台的选型与实践:效率提升背后的技术逻辑 🚀 * 引言:为什么标注平台的选型决定了你与竞品的差距? * 第一部分:选型核心:超越表面的功能清单 * 1. 架构模式:SaaS vs. 私有化部署 ⚖️ * 2. 实时性与并发:WebSocket的魔法 ✨ * 3. 标注格式的“中间态”设计 🎨 * 第二部分:效率提升背后的技术架构 * 数据流转与任务分发架构 * 实战:构建一个智能预标注控制器 (Python示例) * 第三部分:不同数据类型的效率优化技术细节 * 1. 计算机视觉 (CV):交互方式的降维打击 * 2. 自然语言处理 (NLP):LLM如何改变标注剧本? * 第四

By Ne0inhk
人工智能:自然语言处理在客户服务领域的应用与实战

人工智能:自然语言处理在客户服务领域的应用与实战

人工智能:自然语言处理在客户服务领域的应用与实战 学习目标 💡 理解自然语言处理(NLP)在客户服务领域的应用场景和重要性 💡 掌握客户服务领域NLP应用的核心技术(如聊天机器人、意图识别、情感分析) 💡 学会使用前沿模型(如BERT、GPT-3)进行客户服务文本分析 💡 理解客户服务领域的特殊挑战(如实时性要求、多语言处理、用户体验) 💡 通过实战项目,开发一个客户服务聊天机器人应用 重点内容 * 客户服务领域NLP应用的主要场景 * 核心技术(聊天机器人、意图识别、情感分析) * 前沿模型(BERT、GPT-3)在客户服务领域的使用 * 客户服务领域的特殊挑战 * 实战项目:客户服务聊天机器人应用开发 一、客户服务领域NLP应用的主要场景 1.1 聊天机器人 1.1.1 聊天机器人的基本概念 聊天机器人是通过自然语言与用户进行交互的程序。在客户服务领域,聊天机器人的主要应用场景包括: * 客户服务:回答客户的问题(如“如何退货”、“商品价格”

By Ne0inhk
HarmonyOS 游戏开发实践:按钮 + 文字精准控制详解

HarmonyOS 游戏开发实践:按钮 + 文字精准控制详解

个人主页:ujainu 文章目录 * @[TOC] * 一、前言:为什么要精准控制界面? * 二、Text 文字精确控制(8 大核心属性) * 1. 文字内容:静态与动态 * 2. 文字大小:使用 `fontSize` 与 `vp` 单位 * 3. 文字颜色:三种方式 * 4. 文字粗细:`fontWeight` * 5. 文字对齐:`textAlign` * 6. 换行与溢出:`maxLines` + `textOverflow` * 7. 行高与字间距:`lineHeight` 与 `letterSpacing` * 8. 文字阴影:`textShadow` * 三、Button 按钮精确控制(8 大核心属性) * 1. 按钮文字与图标

By Ne0inhk