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

GESP-C++考试(二级)考试重点 (附:【编程题模板】大全)

GESP-C++考试(二级)考试重点 (附:【编程题模板】大全)

一、GESP C++ 二级考试重点 “会写多层 if else  +  多层循环  +  用基础数学函数解决综合问题” (一)理论部分重点(选择 / 判断题高频) 根据大纲,以下内容是必考且高频 GESP大纲: (1)计算机基础 * 存储器 * RAM(内存) vs ROM(只读) * Cache(高速缓存) * 网络 * LAN / MAN / WAN * OSI 七层模型(知道名字) * TCP/IP 四层模型(知道名字) * 程序设计语言 * 机器语言 / 汇编语言 / 高级语言 * C++ 属于高级语言 👉 特点: * 不考计算 * 多为“认识型”“判断型” (2)流程图

By Ne0inhk
《C++ 基础进阶:内存开辟规则、类型转换原理与 IO 流高效使用》

《C++ 基础进阶:内存开辟规则、类型转换原理与 IO 流高效使用》

前引:在 C++ 编程中,内存管理是程序稳定性与性能的基石,而类型转换与 IO 流则是数据处理和交互的核心工具。栈与堆作为内存分配的两大核心区域,其开辟方式直接决定了变量的生命周期、访问效率及内存安全 —— 错误的分配策略可能导致内存泄漏、野指针或栈溢出等致命问题。与此同时,类型转换的合理性关乎类型系统的严谨性,不当转换易引发数据截断、逻辑错误;IO 流作为数据输入输出的桥梁,其正确使用则直接影响程序与外部设备(如控制台、文件)交互的可靠性! 目录 【一】内存完美开辟 (1)栈和堆的本质区别 (2)如何只在栈上开辟空间 (3)如何只在堆上开辟空间 【二】C++的四种类型转换 (1)static_cast (2)reinterpret_cast (3)const_cast (4)dynamic_cast 【三】operator类型转换 (1)

By Ne0inhk
华为OD机试双机位C卷:采购订单 (Py/Java/C/C++/Js/Go)

华为OD机试双机位C卷:采购订单 (Py/Java/C/C++/Js/Go)

采购订单 华为OD机试双机位C卷 - 华为OD上机考试双机位C卷 100分题型 华为OD机试双机位c卷真题目录点击查看: 华为OD机试双机位C卷真题题库目录|机考题库 + 算法考点详解 题目描述 在一个采购系统中,采购申请(PR)需要经过审批后才能生成采购订单(PO)。每个PR包含商品的单价(假设相同商品的单价一定是一样的)及数量信息。系统要求对商品进行分类处理:单价高于100元的商品需要单独处理,单价低于或等于100元的相同商品可以合并到同一采购订单PO中。针对单价低于100的小额订单,如果量大可以打折购买。 具体规则如下: 如果PR状态为"审批通过",则将其商品加入到PO中。如果PR的状态为"审批拒绝"或"待审批",则忽略改PR。 对于单价高于100元的商品,每个商品单独生成一条PO记录。对于单价低于100元的商品,将相同商品的数量合并到一条PO记录中。 如果商品单价<100且商品数量>=100,则单价打9折。 输入描述 第一行包含整数N,

By Ne0inhk