16.面向对象-封装-运算符重载
16.1 知识点
运算符重载的基本概念
概念:让自定义类和结构体能够使用运算符
使用关键字:
operator
特点
- 一定是一个函数(方法)
- 返回值写在
operator
前 - 逻辑处理自定义
- 重载运算符的成员函数,一般用于左操作数是当前类对象时
- 重载运算符的非成员函数,一般用于左操作数不是该类对象时
作用:让自定义类和结构体对象可以进行运算
注意
- 一个符号可以多个重载
- 一些特殊运算符不能重载
- 关系运算符推荐成对实现(比如
==
和!=
;<
和>
等) - 参数数量由符号决定
运算符重载的基本语法
基本语法:
返回类型 operator运算符(参数列表)
成员函数时(左操作数必须是当前类的对象):
返回类型 operator运算符(最多一个参数)
非成员函数时(左操作数可以不是当前类的对象):
返回类型 operator运算符(最多两个参数)
建议
- 为了避免函数调用时产生拷贝开销,建议采用引用的形式传递参数
- 一般重载运算符时,不会在内部修改传入参数,可以善用
const
运算符重载的实例
Point.h 定义,表示二维平面上的点
#pragma once
/**
* Point类 - 表示二维平面上的点,并实现了动态数组功能
* 包含坐标属性(x,y)和一个动态整数数组
*/
class Point
{
public:
int x = 0; // 点在二维平面中的x坐标
int y = 0; // 点在二维平面中的y坐标
// 运算符重载部分:实现Point对象与其他类型的运算
//1.为了节约性能 避免拷贝 可以通过引用传递
//2.但是如果通过引用传递 意味着内部修改了 外部会变
// 那么对于一些常量传入 就会有问题 所以我们可以加上 const
// 如果传入的参数
//关于1、2的总结:
// 只会用不会改,那么都建议大家采用 引用 + const的传递形式
//3.如果想要运算符重载 支持 常量去调用 那么我们需要将其声明为const方法 在方法后加const关键字
// 算数运算符重载
//把自定义类作为左值去+另外类型的内容时
//通过成员方法 去实现即可
//可以让 该类对象 作为左值 和后面指定类型的 数据进行+法运算
// Point对象 + 指定类型的变量 得到一个新的内容(自定义)
/**
* 重载加法运算符 +
* 实现两个Point对象相加,返回一个新的Point对象
* 新对象的坐标为两个原对象坐标之和,数组内容不变
*/
Point operator+(const Point& other) const;
/**
* 重载加法运算符 +
* 实现Point对象与整数相加,返回一个新的Point对象
* 新对象的坐标为原对象坐标加上该整数,数组内容不变
*/
Point operator+(const int& i) const;
/**
* 重载加法运算符 +
* 实现Point对象与浮点数相加,返回浮点数结果
* 结果为两个对象坐标的欧氏距离加上浮点数
*/
float operator+(const float& other) const;
// 关系运算符重载:用于比较两个Point对象
/**
* 重载等于运算符 ==
* 判断两个Point对象是否相等
* 当且仅当坐标(x,y)和数组内容完全相同时返回true
*/
bool operator==(const Point& other) const;
/**
* 重载不等于运算符 !=
* 判断两个Point对象是否不相等
* 是operator==的逻辑取反
*/
bool operator!=(const Point& other) const;
/**
* 重载逻辑非运算符 !
* 判断Point对象是否为空
* 当坐标为(0,0)且数组为空时返回true
*/
bool operator!() const;
/**
* 重载逻辑与运算符 &&
* 实现两个Point对象的逻辑与运算
* 当两个对象的坐标都非零且数组大小相同时返回true
*/
bool operator&&(const Point& other) const;
// 自增运算符重载:用于对Point对象进行递增操作
/**
* 重载前置自增运算符 ++
* 将Point对象的坐标(x,y)各增加1
*/
void operator++();
/**
* 重载后置自增运算符 ++
* 将Point对象的坐标(x,y)各增加1
* int参数仅用于区分前置和后置自增,无实际意义
*/
void operator++(int);
/**
* 重载按位取反运算符 ~
* 对Point对象的坐标进行取反操作
* 即 x = -x - 1, y = -y - 1
*/
void operator~();
/**
* 重载复合赋值运算符 +=
* 将Point对象的坐标(x,y)各加上指定整数
*/
void operator+=(const int& i);
// 拓展索引器 使用[] 快速访问内部数组相关容器
int* data; // 动态数组首地址,存储整数数据
int size; // 当前数组中已存储的元素数量
int capacity; // 数组的总容量
/**
* 默认构造函数
* 初始化一个容量为10的整数数组
*/
Point()
{
data = new int[10] {0}; // 分配10个元素的内存空间并初始化为0
size = 0; // 初始元素数量为0
capacity = 10; // 数组初始容量为10
}
/**
* 带参数的构造函数
* @param num 数组的初始容量
*/
Point(int num)
{
data = new int[num] {0}; // 分配指定容量的内存空间并初始化为0
size = 0; // 初始元素数量为0
capacity = num; // 数组容量设置为指定值
}
/**
* 重载下标运算符 []
* 实现通过索引访问数组中的元素
* @param index 数组索引,范围从0到size-1
* @return 指定索引处的数组元素值
*/
int operator[](const int& index) const;
};
Point.cpp 实现运算符重载具体逻辑
#include <iostream>
#include "Point.h"
using namespace std;
/**
* 重载加法运算符 +(Point + Point)
* 将两个Point对象的坐标相加,返回新的Point对象
* 示例:Point p3 = p1 + p2;
*/
Point Point::operator+(const Point& other) const
{
// 自定义加法逻辑:对应坐标相加
Point p;
p.x = this->x + other.x; // x坐标相加
p.y = this->y + other.y; // y坐标相加
return p;
}
/**
* 重载加法运算符 +(Point + int)
* 将Point对象的坐标加上一个整数,返回新的Point对象
* 示例:Point p2 = p1 + 5;
*/
Point Point::operator+(const int& i) const
{
// 自定义加法逻辑:坐标各加上整数i
Point p;
p.x = this->x + i; // x坐标加i
p.y = this->y + i; // y坐标加i
return p;
}
/**
* 重载加法运算符 +(Point + float)
* 将Point对象的坐标和浮点数相加,返回浮点数结果
* 示例:float result = p1 + 3.14f;
*/
float Point::operator+(const float& other) const
{
// 自定义加法逻辑:x坐标 + y坐标 + 浮点数
return this->x + this->y + other;
}
/**
* 重载等于运算符 ==
* 判断两个Point对象的坐标是否相等
* 示例:if(p1 == p2) { ... }
*/
bool Point::operator==(const Point& other) const
{
// 判断条件:x坐标和y坐标都相等
if (this->x == other.x &&
this->y == other.y)
return true;
return false;
}
/**
* 重载不等于运算符 !=
* 判断两个Point对象的坐标是否不相等
* 示例:if(p1 != p2) { ... }
*/
bool Point::operator!=(const Point& other) const
{
// 判断条件:x坐标或y坐标不相等
if (this->x != other.x ||
this->y != other.y)
return true;
return false;
}
/**
* 重载逻辑非运算符 !
* 判断Point对象的坐标是否都不大于0
* 示例:if(!p1) { ... }
*/
bool Point::operator!() const
{
// 自定义逻辑:x或y大于0时返回true
if (this->x > 0 ||
this->y > 0)
return true;
return false;
}
/**
* 重载逻辑与运算符 &&
* 判断两个Point对象的坐标之和是否都大于0
* 示例:if(p1 && p2) { ... }
*/
bool Point::operator&&(const Point& other) const
{
// 自定义逻辑:两个对象的x坐标之和和y坐标之和都大于0
if (this->x + other.x > 0 &&
this->y + other.y > 0)
return true;
return false;
}
/**
* 重载前置自增运算符 ++
* 将Point对象的坐标各增加1
* 示例:++p1;
*/
void Point::operator++()
{
this->x += 1; // x坐标自增1
this->y += 1; // y坐标自增1
}
/**
* 重载后置自增运算符 ++
* 将Point对象的坐标各增加1
* 示例:p1++;
* 注意:int参数仅用于区分前置和后置,无实际意义
*/
void Point::operator++(int)
{
this->x += 1; // x坐标自增1
this->y += 1; // y坐标自增1
}
/**
* 重载按位取反运算符 ~
* 对Point对象的坐标进行按位取反操作
* 示例:~p1;
*/
void Point::operator~()
{
this->x = ~this->x; // 按位取反x坐标
this->y = ~this->y; // 按位取反y坐标
}
/**
* 重载复合赋值运算符 +=
* 将Point对象的坐标各加上指定整数
* 示例:p1 += 5;
*/
void Point::operator+=(const int& i)
{
this->x += i; // x坐标加上i
this->y += i; // y坐标加上i
}
/**
* 重载下标运算符 []
* 通过索引访问Point对象内部数组的元素
* 示例:int value = p1[2];
* @param index 数组索引(0至size-1有效)
* @return 对应索引位置的元素值,越界时返回-1
*/
int Point::operator[](const int& index) const
{
// 检查索引是否越界
if (index >= capacity)
{
cout << "越界了 数组没那么多空间" << endl;
return -1;
}
else if (index >= size)
{
cout << "这个数组这个位置还没有装载内容" << endl;
return -1;
}
else
return this->data[index]; // 返回对应位置的元素
}
// 非成员函数的运算符重载:允许其他类型 + Point对象
// 非成员函数的运算符重载的主要作用
// 就是可以让一个指定类型的变量 + 自定义类型的变量
// 具体返回什么 由我们自己决定
// 让一个目标类型 + 自定义类型 让它可以进行运算符的计算
/**
* 重载加法运算符 +(int + Point)
* 将整数和Point对象的坐标相加,返回整数结果
* 示例:int result = 5 + p1;
*/
int operator+(const int& i, const Point& p)
{
return i + p.x + p.y; // 整数加上x和y坐标的和
}
/**
* 重载加法运算符 +(float + Point)
* 将浮点数和Point对象的坐标相加,返回新的Point对象
* 示例:Point p2 = 3.14f + p1;
*/
Point operator+(const float& f, const Point& p)
{
Point point;
point.x = f + p.x; // x坐标为浮点数加原x坐标
point.y = f + p.y; // y坐标为浮点数加原y坐标
return point;
}
/**
* 重载小于运算符 <(int < Point)
* 判断整数是否小于Point对象的任一坐标
* 示例:if(5 < p1) { ... }
*/
bool operator<(const int& i, const Point& p)
{
// 判断条件:整数小于x坐标或y坐标
if (i < p.x ||
i < p.y)
return true;
return false;
}
/**
* 重载复合赋值运算符 +=(int += Point)
* 将Point对象的坐标和加到整数上
* 示例:i += p1;
*/
void operator+=(int& i, const Point& p)
{
i += p.x + p.y; // 整数加上x和y坐标的和
}
使用
#pragma region 可重载的运算符
#pragma region 算数运算符
// + - * / %
// 主要作用:让自定义对象可以进行计算
Point p;
p.x = 5;
p.y = 5;
Point p2;
p2.x = 3;
p2.y = 3;
// 只要实现了运算符重载的成员函数,就可以将自定义类作为左值去加上实现了类型的对象了
Point p3 = p + p2; // 5+3=8, 5+3=8
cout << p3.x << endl; // 输出: 8
cout << p3.y << endl; // 输出: 8
p3 = p3 + 10; // 8+10=18, 8+10=18
cout << p3.x << endl; // 输出: 18
cout << p3.y << endl; // 输出: 18
float f = p3 + 5.5f; // 18+18+5.5=41.5
cout << f << endl; // 输出: 41.5
// 这是非成员函数的运算符重载
int i = 10 + p3; // 10+18+18=46
cout << i << endl; // 输出: 46
Point p4 = 4.5f + p3; // 4.5+18=22.5(取整为22), 4.5+18=22.5(取整为22)
cout << p4.x << endl; // 输出: 22
cout << p4.y << endl; // 输出: 22
const Point p5; // 默认构造,x=0, y=0
Point p6 = p5 + 10; // 0+10=10, 0+10=10
#pragma endregion
#pragma region 关系运算符
// == != < > <= >=
// 主要作用:让自定义对象可以进行比较,建议配对实现,避免使用时有限制
if (p3 == p4) // 18 != 22
cout << "相等" << endl; // 不输出
if (p3 != p4) // 18 != 22
cout << "不相等" << endl; // 输出: 不相等
if (1 < p4) // 1 < 22 或 1 < 22
cout << "小于" << endl; // 输出: 小于
#pragma endregion
#pragma region 逻辑运算符
// ! && ||
// 主要作用:让自定义对象可以进行逻辑运算 是否有参数,根据运算符需要几个参数参与计算来决定
if (p3 && p4) // 18+22>0 且 18+22>0
cout << "逻辑与返回true" << endl; // 输出: 逻辑与返回true
if (!p3) // !(18>0 || 18>0)
cout << "逻辑非返回true" << endl; // 不输出
#pragma endregion
#pragma region 自增减
// ++ --
// 主要作用:
// 让自定义对象可以进行自增减
// 用int标记表示后置符号
cout << p3.x << endl; // 输出: 18
cout << p3.y << endl; // 输出: 18
++p3; // 18+1=19, 18+1=19
cout << p3.x << endl; // 输出: 19
cout << p3.y << endl; // 输出: 19
p3++; // 19+1=20, 19+1=20
cout << p3.x << endl; // 输出: 20
cout << p3.y << endl; // 输出: 20
#pragma endregion
#pragma region 位运算符
// << >> ~ & | ^
// 主要作用:让自定义对象可以进行位运算
// 对于<< >>也可以不用做位运算,用来处理其他逻辑
~p3; // ~20=-21, ~20=-21
cout << p3.x << endl; // 输出: -21
cout << p3.y << endl; // 输出: -21
#pragma endregion
#pragma region 赋值运算符
// = += -= *= /= %= &= |= ^= <<= >>=
// 主要作用:让自定义对象可以进行赋值运算符
p3 += 10; // -21+10=-11, -21+10=-11
cout << p3.x << endl; // 输出: -11
cout << p3.y << endl; // 输出: -11
int ii = 10;
ii += p3; // 10+(-11)+(-11)=-12
cout << ii << endl; // 输出: -12
#pragma endregion
#pragma region 其他
// [] () -> ->* new delete new[] delete[] ,
// 主要作用:可以利用这些符号拓展一些功能
// 比如 [] 一般用来快速访问内部数组相关容器
Point p10 = Point(30); // 创建容量为30的Point对象
cout << p10[100] << endl; // 越界,输出: 越界了 数组没那么多空间 -1
#pragma endregion
#pragma region 不可重载的运算符
// ::
// .
// .*
// ?
// :
// 等
#pragma endregion
总结
运算符重载本质是函数(方法)
只要我们重载了运算符,那么当执行运算符逻辑时本质上就是在执行方法
关键字:
operator
成员函数和非成员函数的区别
- 成员函数:自定义对象作为左值
- 非成员函数:可以让指定类型作为左值和自定义对象的右值进行计算
具体重载函数的参数由符号本身的规则决定,如果它需要两个值进行计算,就会有参数;否则不需要参数
++
--
加上int
标记可以实现后置计算(符号放到后面),不加就是前置计算(符号放到前面)const
和&
一定要根据需求合理使用,可有效提升效率,在方法后加const
可以支持常量调用
16.2 知识点代码
Point.h
#pragma once
/**
* Point类 - 表示二维平面上的点,并实现了动态数组功能
* 包含坐标属性(x,y)和一个动态整数数组
*/
class Point
{
public:
int x = 0; // 点在二维平面中的x坐标
int y = 0; // 点在二维平面中的y坐标
// 运算符重载部分:实现Point对象与其他类型的运算
//1.为了节约性能 避免拷贝 可以通过引用传递
//2.但是如果通过引用传递 意味着内部修改了 外部会变
// 那么对于一些常量传入 就会有问题 所以我们可以加上 const
// 如果传入的参数
//关于1、2的总结:
// 只会用不会改,那么都建议大家采用 引用 + const的传递形式
//3.如果想要运算符重载 支持 常量去调用 那么我们需要将其声明为const方法 在方法后加const关键字
// 算数运算符重载
//把自定义类作为左值去+另外类型的内容时
//通过成员方法 去实现即可
//可以让 该类对象 作为左值 和后面指定类型的 数据进行+法运算
// Point对象 + 指定类型的变量 得到一个新的内容(自定义)
/**
* 重载加法运算符 +
* 实现两个Point对象相加,返回一个新的Point对象
* 新对象的坐标为两个原对象坐标之和,数组内容不变
*/
Point operator+(const Point& other) const;
/**
* 重载加法运算符 +
* 实现Point对象与整数相加,返回一个新的Point对象
* 新对象的坐标为原对象坐标加上该整数,数组内容不变
*/
Point operator+(const int& i) const;
/**
* 重载加法运算符 +
* 实现Point对象与浮点数相加,返回浮点数结果
* 结果为两个对象坐标的欧氏距离加上浮点数
*/
float operator+(const float& other) const;
// 关系运算符重载:用于比较两个Point对象
/**
* 重载等于运算符 ==
* 判断两个Point对象是否相等
* 当且仅当坐标(x,y)和数组内容完全相同时返回true
*/
bool operator==(const Point& other) const;
/**
* 重载不等于运算符 !=
* 判断两个Point对象是否不相等
* 是operator==的逻辑取反
*/
bool operator!=(const Point& other) const;
/**
* 重载逻辑非运算符 !
* 判断Point对象是否为空
* 当坐标为(0,0)且数组为空时返回true
*/
bool operator!() const;
/**
* 重载逻辑与运算符 &&
* 实现两个Point对象的逻辑与运算
* 当两个对象的坐标都非零且数组大小相同时返回true
*/
bool operator&&(const Point& other) const;
// 自增运算符重载:用于对Point对象进行递增操作
/**
* 重载前置自增运算符 ++
* 将Point对象的坐标(x,y)各增加1
*/
void operator++();
/**
* 重载后置自增运算符 ++
* 将Point对象的坐标(x,y)各增加1
* int参数仅用于区分前置和后置自增,无实际意义
*/
void operator++(int);
/**
* 重载按位取反运算符 ~
* 对Point对象的坐标进行取反操作
* 即 x = -x - 1, y = -y - 1
*/
void operator~();
/**
* 重载复合赋值运算符 +=
* 将Point对象的坐标(x,y)各加上指定整数
*/
void operator+=(const int& i);
//拓展索引器 使用[] 快速访问内部数组相关容器
int* data; // 动态数组首地址,存储整数数据
int size; // 当前数组中已存储的元素数量
int capacity; // 数组的总容量
/**
* 默认构造函数
* 初始化一个容量为10的整数数组
*/
Point()
{
data = new int[10] {0}; // 分配10个元素的内存空间并初始化为0
size = 0; // 初始元素数量为0
capacity = 10; // 数组初始容量为10
}
/**
* 带参数的构造函数
* @param num 数组的初始容量
*/
Point(int num)
{
data = new int[num] {0}; // 分配指定容量的内存空间并初始化为0
size = 0; // 初始元素数量为0
capacity = num; // 数组容量设置为指定值
}
/**
* 重载下标运算符 []
* 实现通过索引访问数组中的元素
* @param index 数组索引,范围从0到size-1
* @return 指定索引处的数组元素值
*/
int operator[](const int& index) const;
};
Point.cpp
#include <iostream>
#include "Point.h"
using namespace std;
/**
* 重载加法运算符 +(Point + Point)
* 将两个Point对象的坐标相加,返回新的Point对象
* 示例:Point p3 = p1 + p2;
*/
Point Point::operator+(const Point& other) const
{
// 自定义加法逻辑:对应坐标相加
Point p;
p.x = this->x + other.x; // x坐标相加
p.y = this->y + other.y; // y坐标相加
return p;
}
/**
* 重载加法运算符 +(Point + int)
* 将Point对象的坐标加上一个整数,返回新的Point对象
* 示例:Point p2 = p1 + 5;
*/
Point Point::operator+(const int& i) const
{
// 自定义加法逻辑:坐标各加上整数i
Point p;
p.x = this->x + i; // x坐标加i
p.y = this->y + i; // y坐标加i
return p;
}
/**
* 重载加法运算符 +(Point + float)
* 将Point对象的坐标和浮点数相加,返回浮点数结果
* 示例:float result = p1 + 3.14f;
*/
float Point::operator+(const float& other) const
{
// 自定义加法逻辑:x坐标 + y坐标 + 浮点数
return this->x + this->y + other;
}
/**
* 重载等于运算符 ==
* 判断两个Point对象的坐标是否相等
* 示例:if(p1 == p2) { ... }
*/
bool Point::operator==(const Point& other) const
{
// 判断条件:x坐标和y坐标都相等
if (this->x == other.x &&
this->y == other.y)
return true;
return false;
}
/**
* 重载不等于运算符 !=
* 判断两个Point对象的坐标是否不相等
* 示例:if(p1 != p2) { ... }
*/
bool Point::operator!=(const Point& other) const
{
// 判断条件:x坐标或y坐标不相等
if (this->x != other.x ||
this->y != other.y)
return true;
return false;
}
/**
* 重载逻辑非运算符 !
* 判断Point对象的坐标是否都不大于0
* 示例:if(!p1) { ... }
*/
bool Point::operator!() const
{
// 自定义逻辑:x或y大于0时返回true
if (this->x > 0 ||
this->y > 0)
return true;
return false;
}
/**
* 重载逻辑与运算符 &&
* 判断两个Point对象的坐标之和是否都大于0
* 示例:if(p1 && p2) { ... }
*/
bool Point::operator&&(const Point& other) const
{
// 自定义逻辑:两个对象的x坐标之和和y坐标之和都大于0
if (this->x + other.x > 0 &&
this->y + other.y > 0)
return true;
return false;
}
/**
* 重载前置自增运算符 ++
* 将Point对象的坐标各增加1
* 示例:++p1;
*/
void Point::operator++()
{
this->x += 1; // x坐标自增1
this->y += 1; // y坐标自增1
}
/**
* 重载后置自增运算符 ++
* 将Point对象的坐标各增加1
* 示例:p1++;
* 注意:int参数仅用于区分前置和后置,无实际意义
*/
void Point::operator++(int)
{
this->x += 1; // x坐标自增1
this->y += 1; // y坐标自增1
}
/**
* 重载按位取反运算符 ~
* 对Point对象的坐标进行按位取反操作
* 示例:~p1;
*/
void Point::operator~()
{
this->x = ~this->x; // 按位取反x坐标
this->y = ~this->y; // 按位取反y坐标
}
/**
* 重载复合赋值运算符 +=
* 将Point对象的坐标各加上指定整数
* 示例:p1 += 5;
*/
void Point::operator+=(const int& i)
{
this->x += i; // x坐标加上i
this->y += i; // y坐标加上i
}
/**
* 重载下标运算符 []
* 通过索引访问Point对象内部数组的元素
* 示例:int value = p1[2];
* @param index 数组索引(0至size-1有效)
* @return 对应索引位置的元素值,越界时返回-1
*/
int Point::operator[](const int& index) const
{
// 检查索引是否越界
if (index >= capacity)
{
cout << "越界了 数组没那么多空间" << endl;
return -1;
}
else if (index >= size)
{
cout << "这个数组这个位置还没有装载内容" << endl;
return -1;
}
else
return this->data[index]; // 返回对应位置的元素
}
//非成员函数的运算符重载:允许其他类型 + Point对象
//非成员函数的运算符重载的主要作用
//就是可以让一个指定类型的变量 + 自定义类型的变量
//具体返回什么 由我们自己决定
//让一个目标类型 + 自定义类型 让它可以进行运算符的计算
/**
* 重载加法运算符 +(int + Point)
* 将整数和Point对象的坐标相加,返回整数结果
* 示例:int result = 5 + p1;
*/
int operator+(const int& i, const Point& p)
{
return i + p.x + p.y; // 整数加上x和y坐标的和
}
/**
* 重载加法运算符 +(float + Point)
* 将浮点数和Point对象的坐标相加,返回新的Point对象
* 示例:Point p2 = 3.14f + p1;
*/
Point operator+(const float& f, const Point& p)
{
Point point;
point.x = f + p.x; // x坐标为浮点数加原x坐标
point.y = f + p.y; // y坐标为浮点数加原y坐标
return point;
}
/**
* 重载小于运算符 <(int < Point)
* 判断整数是否小于Point对象的任一坐标
* 示例:if(5 < p1) { ... }
*/
bool operator<(const int& i, const Point& p)
{
// 判断条件:整数小于x坐标或y坐标
if (i < p.x ||
i < p.y)
return true;
return false;
}
/**
* 重载复合赋值运算符 +=(int += Point)
* 将Point对象的坐标和加到整数上
* 示例:i += p1;
*/
void operator+=(int& i, const Point& p)
{
i += p.x + p.y; // 整数加上x和y坐标的和
}
Lesson16_面向对象_封装_运算符重载.cpp
#include <iostream>
#include "Point.h"
using namespace std;
//非成员函数的运算符重载需要声明一下
int operator+(const int& i, const Point& p);
Point operator+(const float& f, const Point& p);
bool operator<(const int& i, const Point& p);
void operator+=(int& i, const Point& p);
int main()
{
#pragma region 知识点一 运算符重载的基本概念
//概念
//让自定义类和结构体
//能够使用运算符
//使用关键字
//operator
//特点
//1.一定是一个函数(方法)
//2.返回值写在 operator 前
//3.逻辑处理自定义
//4.重载运算符的成员函数,一般用于左操作数是当前类对象时
//5.重载运算符的非成员函数,一般用于左操作数不是该类对象时
//作用
//让自定义类和结构体对象可以进行运算
//注意:
//1.一个符号可以多个重载
//2.一些特殊运算符不能重载
//3.关系运算符推荐成对实现(比如 == 和 !=; < 和 > 等)
//4.参数数量由符号决定
#pragma endregion
#pragma region 知识点二 运算符重载的基本语法
//基本语法:
//返回类型 operator运算符(参数列表)
//成员函数时(左操作数必须是当前类的对象):
//返回类型 operator运算符(最多一个参数)
//非成员函数时(左操作数可以不是当前类的对象):
//返回类型 operator运算符(最多两个参数)
//建议:
//1.为了避免函数调用时产生拷贝开销,建议采用引用的形式传递参数
//2.一般重载运算符时,不会在内部修改传入参数,可以善用const
#pragma endregion
#pragma region 知识点三 运算符重载的实例
#pragma region 可重载的运算符
#pragma region 算数运算符
//+ - * / %
//主要作用:
//让自定义对象可以进行计算
Point p;
p.x = 5;
p.y = 5;
Point p2;
p2.x = 3;
p2.y = 3;
//只要实现了运算符重载的成员函数 就可以将自定义类作为左值 去加上实现了类型的对象了
Point p3 = p + p2; // 5+3=8, 5+3=8
cout << p3.x << endl; // 输出: 8
cout << p3.y << endl; // 输出: 8
p3 = p3 + 10; // 8+10=18, 8+10=18
cout << p3.x << endl; // 输出: 18
cout << p3.y << endl; // 输出: 18
float f = p3 + 5.5f; // 18+18+5.5=41.5
cout << f << endl; // 输出: 41.5
//这是非成员函数的运算符重载
int i = 10 + p3; // 10+18+18=46
cout << i << endl; // 输出: 46
Point p4 = 4.5f + p3; // 4.5+18=22.5(取整为22), 4.5+18=22.5(取整为22)
cout << p4.x << endl; // 输出: 22
cout << p4.y << endl; // 输出: 22
const Point p5; // 默认构造,x=0, y=0
Point p6 = p5 + 10; // 0+10=10, 0+10=10
#pragma endregion
#pragma region 关系运算符
//== != < > <= >=
//主要作用:
//让自定义对象可以进行比较,建议配对实现,避免使用时有限制
if (p3 == p4) // 18 != 22
cout << "相等" << endl; // 不输出
if (p3 != p4) // 18 != 22
cout << "不相等" << endl; // 输出: 不相等
if (1 < p4) // 1 < 22 或 1 < 22
cout << "小于" << endl; // 输出: 小于
#pragma endregion
#pragma region 逻辑运算符
//! && ||
//主要作用:
//让自定义对象可以进行逻辑运算 是否有参数,根据运算符需要几个参数参与计算来决定
if (p3 && p4) // 18+22>0 且 18+22>0
cout << "逻辑与返回true" << endl; // 输出: 逻辑与返回true
if (!p3) // !(18>0 || 18>0)
cout << "逻辑非返回true" << endl; // 不输出
#pragma endregion
#pragma region 自增减
//++ --
//主要作用:
//让自定义对象可以进行自增减
//用int标记表示后置符号
cout << p3.x << endl; // 输出: 18
cout << p3.y << endl; // 输出: 18
++p3; // 18+1=19, 18+1=19
cout << p3.x << endl; // 输出: 19
cout << p3.y << endl; // 输出: 19
p3++; // 19+1=20, 19+1=20
cout << p3.x << endl; // 输出: 20
cout << p3.y << endl; // 输出: 20
#pragma endregion
#pragma region 位运算符
//<< >> ~ & | ^
//主要作用:
//让自定义对象可以进行位运算
//对于<< >>也可以不用做位运算,用来处理其他逻辑
~p3; // ~20=-21, ~20=-21
cout << p3.x << endl; // 输出: -21
cout << p3.y << endl; // 输出: -21
#pragma endregion
#pragma region 赋值运算符
//= += -= *= /= %= &= |= ^= <<= >>=
//主要作用:
//让自定义对象可以进行赋值运算符
p3 += 10; // -21+10=-11, -21+10=-11
cout << p3.x << endl; // 输出: -11
cout << p3.y << endl; // 输出: -11
int ii = 10;
ii += p3; // 10+(-11)+(-11)=-12
cout << ii << endl; // 输出: -12
#pragma endregion
#pragma region 其他
//[] () -> ->* new delete new[] delete[] ,
//主要作用:
//可以利用这些符号拓展一些功能
//比如 [] 一般用来快速访问内部数组相关容器
Point p10 = Point(30); // 创建容量为30的Point对象
cout << p10[100] << endl; // 越界,输出: 越界了 数组没那么多空间 -1
#pragma endregion
#pragma endregion
#pragma region 不可重载的运算符
//::
//.
//.*
//?
//:
//等
#pragma endregion
#pragma endregion
//总结
//1.运算符重载本质是函数(方法)
//2.只要我们重载了运算符,那么当执行运算符逻辑时本质上就是在执行方法
//3.operator
//4.成员函数 和 非成员函数的区别
// 自定对象作为左值 可以让指定类型作为左值和自定义对象的右值进行计算
//5.具体重载函数的参数 是由符号本身的规则决定的 如果它需要两个值进行计算 那么一般就会有参数
// 否则就不需要参数
//6. ++ -- 加上int标记 就可以后置(符号放到后面)计算了,不加 就是前置计算(符号放到前面)
//7.const 和 & 一定要合理根据需求使用,可以有效的提升效率 还可以在函数后加const 可以支持常量调用
}
// 练习题代码块
// 练习题代码块
16.3 练习题
定义一个位置结构体或类,为其重载判断是否相等的运算符
头文件
#pragma once
class Postion
{
public:
int x;
int y;
Postion(int x, int y) :x(x), y(y)
{
}
bool operator==(const Postion& pos) const;
bool operator!=(const Postion& pos) const;
};
cpp 文件
#include "Postion.h"
bool Postion::operator==(const Postion& pos) const
{
if (this->x == pos.x &&
this->y == pos.y)
return true;
return false;
}
bool Postion::operator!=(const Postion& pos) const
{
if (this->x != pos.x ||
this->y != pos.y)
return true;
return false;
}
使用
Postion p = Postion(10, 10);
Postion p2 = Postion(10, 10);
if (p == p2)
cout << "相等" << endl;
else
cout << "不相等" << endl;
if (p != p2)
cout << "不相等" << endl;
else
cout << "相等" << endl;
定义一个 Vector3 类(x,y,z),通过重载运算符实现以下运算
(x1,y1,z1) + (x2,y2,z2) = (x1+x2,y1+y2,z1+z2)
(x1,y1,z1) - (x2,y2,z2) = (x1-x2,y1-y2,z1-z2)
(x1,y1,z1) * num = (x1num,y1num,z1*num)
头文件
#pragma once
class Vector3
{
public:
int x;
int y;
int z;
Vector3(int x, int y, int z) :x(x), y(y), z(z) {}
Vector3 operator+(const Vector3& other) const;
Vector3 operator-(const Vector3& other) const;
Vector3 operator*(const int& num) const;
};
cpp 文件
#include "Vector3.h"
Vector3 Vector3::operator+(const Vector3& other) const
{
return Vector3(this->x + other.x, this->y + other.y, this->z + other.z);
}
Vector3 Vector3::operator-(const Vector3& other) const
{
return Vector3(this->x - other.x, this->y - other.y, this->z - other.z);
}
Vector3 Vector3::operator*(const int& num) const
{
return Vector3(this->x * num, this->y * num, this->z * num);
}
Vector3 operator*(const int& num, const Vector3& pos)
{
return Vector3(pos.x * num, pos.y * num, pos.z * num);
}
使用
// 创建三维向量对象 v(1,1,1) 和 v2(2,2,2)
Vector3 v = Vector3(1, 1, 1);
Vector3 v2 = Vector3(2, 2, 2);
// 向量加法:v + v2(对应分量相加)
Vector3 v3 = v + v2;
cout << v3.x << "," << v3.y << "," << v3.z << endl; // 输出: 3,3,3
// 向量减法:v - v2(对应分量相减)
Vector3 v4 = v - v2;
cout << v4.x << "," << v4.y << "," << v4.z << endl; // 输出: -1,-1,-1
// 向量数乘(向量在前):v4 * 10(每个分量乘以10)
Vector3 v5 = v4 * 10;
cout << v5.x << "," << v5.y << "," << v5.z << endl; // 输出: -10,-10,-10
// 向量数乘(标量在前):10 * v4(通过非成员运算符重载实现)
v5 = 10 * v4;
cout << v5.x << "," << v5.y << "," << v5.z << endl; // 输出: -10,-10,-10
自定义一个整形数组类,该类中有一个整形数组变量,为它封装增删查改的方法
头文件
#pragma once
class IntArray
{
private:
int* data; // 存储数组首地址
int length; // 当前数组内元素数量
int capacity; // 数组总容量,满时需扩容
public:
IntArray();
IntArray(int capacity);
~IntArray();
// 增:在数组尾部添加
void addValue(int value);
// 删:按位置删除
void removeAt(int index);
// 删:按值删除第一个匹配元素
void remove(int num);
// 查:下标访问
int operator[](int index);
// 查:获取当前长度
int getLength();
// 改:设置指定位置值
void setValue(int index, int value);
};
cpp 文件
#include "IntArray.h"
#include <iostream>
using namespace std;
/**
* 默认构造函数,委托给带参数构造,初始化容量为10的数组
*/
IntArray::IntArray():IntArray(10)
{
}
/**
* 带容量参数构造函数
* @param capacity 数组初始容量
*/
IntArray::IntArray(int capacity)
{
data = new int[capacity];
length = 0;
this->capacity = capacity;
}
/**
* 析构函数,释放动态内存防止泄漏
*/
IntArray::~IntArray()
{
delete[] data;
data = nullptr;
}
/**
* 重载下标运算符,实现索引访问
* @param index 元素索引
* @return 对应元素值,越界返回 -1
*/
int IntArray::operator[](int index)
{
if (index < 0 || index >= length)
{
cout << "访问越界了" << endl;
return -1;
}
return data[index];
}
/**
* 获取当前有效元素数量
* @return 数组长度
*/
int IntArray::getLength()
{
return length;
}
/**
* 修改指定位置元素值
* @param index 元素索引
* @param value 新值
*/
void IntArray::setValue(int index, int value)
{
if (index < 0 || index >= length)
{
cout << "访问越界了" << endl;
return;
}
data[index] = value;
}
/**
* 在尾部添加元素
* @param value 待添加值
*/
void IntArray::addValue(int value)
{
if (length < capacity)
{
data[length++] = value;
}
else
{
int* newData = new int[capacity * 2];
for (int i = 0; i < capacity; i++)
newData[i] = data[i];
delete[] data;
data = newData;
capacity *= 2;
data[length++] = value;
}
}
/**
* 删除指定位置元素
* @param index 待删除元素索引
*/
void IntArray::removeAt(int index)
{
if (index < 0 || index >= length)
{
cout << "越界了" << endl;
return;
}
for (int i = index; i < length - 1; i++)
data[i] = data[i + 1];
--length;
}
/**
* 删除第一个匹配值的元素
* @param num 待删除值
*/
void IntArray::remove(int num)
{
for (int i = 0; i < length; i++)
{
if (data[i] == num)
{
removeAt(i);
return;
}
}
}
使用
// 封装的好处:无需关心数组容量限制,可自动扩容
IntArray array = IntArray(); // 创建初始容量为10的数组
// 向数组尾部添加元素(共7次添加)
array.addValue(100); // 添加100,当前数组:[100]
array.addValue(200); // 添加200,当前数组:[100, 200]
array.addValue(300); // 添加300,当前数组:[100, 200, 300]
array.addValue(400); // 添加400,当前数组:[100, 200, 300, 400]
array.addValue(500); // 添加500,当前数组:[100, 200, 300, 400, 500]
array.addValue(600); // 添加600,当前数组:[100, 200, 300, 400, 500, 600]
array.addValue(500); // 添加500,当前数组:[100, 200, 300, 400, 500, 600, 500]
// 遍历打印所有元素
for (int i = 0; i < array.getLength(); i++)
{
cout << array[i] << endl; // 输出顺序:100 200 300 400 500 600 500
}
// 删除索引2的元素(值为300)
array.removeAt(2); // 数组变为:[100, 200, 400, 500, 600, 500]
// 再次遍历打印
for (int i = 0; i < array.getLength(); i++)
{
cout << array[i] << endl; // 输出顺序:100 200 400 500 600 500
}
// 删除第一个值为500的元素(索引3的值为500)
array.remove(500); // 数组变为:[100, 200, 400, 600, 500]
// 再次遍历打印
for (int i = 0; i < array.getLength(); i++)
{
cout << array[i] << endl; // 输出顺序:100 200 400 600 500
}
// 将索引1的值修改为888
array.setValue(1, 888); // 数组变为:[100, 888, 400, 600, 500]
// 最终遍历打印
for (int i = 0; i < array.getLength(); i++)
{
cout << array[i] << endl; // 输出顺序:100 888 400 600 500
}
16.4 练习题代码
IntArray.h
#pragma once
class IntArray
{
//封装int数组
private:
int* data;//存储数组首地址
int length;//代表当前数组装载了多少个元素
int capacity;//数组的总容量 当想要添加更多的内容 就需要去扩容
public:
IntArray();
IntArray(int capacity);
~IntArray();
//增 朝我们数组可用的尾部去进行添加
void addValue(int value);
//删
//想要删除指定位置的元素
void removeAt(int index);
//想要删除指定元素
void remove(int num);
//查
int operator[](int index);
int getLength();
//改
void setValue(int index, int value);
};
IntArray.cpp
#include "IntArray.h"
#include <iostream>
using namespace std;
/**
* 默认构造函数,委托给带参数的构造函数初始化容量为10的数组
*/
IntArray::IntArray() :IntArray(10)
{
}
/**
* 带容量参数的构造函数
* @param capacity 数组初始容量
*/
IntArray::IntArray(int capacity)
{
// 初始化动态数组,分配指定容量的内存空间
data = new int[capacity];
// 初始元素数量为0
length = 0;
// 保存数组总容量
this->capacity = capacity;
}
/**
* 析构函数
* 释放动态分配的内存,防止内存泄漏
*/
IntArray::~IntArray()
{
// 释放数组内存
delete[] data;
// 指针置空,避免野指针
data = nullptr;
}
/**
* 重载下标运算符 []
* 实现通过索引访问数组元素
* @param index 元素索引
* @return 对应位置的元素值,越界时返回-1
*/
int IntArray::operator[](int index)
{
// 检查索引合法性
if (index < 0 || index >= length)
{
cout << "访问越界了" << endl;
return -1;
}
return data[index];
}
/**
* 获取当前数组的有效元素数量
* @return 数组长度
*/
int IntArray::getLength()
{
return length;
}
/**
* 修改指定位置的元素值
* @param index 元素索引
* @param value 新值
*/
void IntArray::setValue(int index, int value)
{
// 检查索引合法性
if (index < 0 || index >= length)
{
cout << "访问越界了" << endl;
return;
}
// 修改对应位置的值
data[index] = value;
}
/**
* 向数组尾部添加元素
* @param value 待添加的值
*/
void IntArray::addValue(int value)
{
// 检查是否还有空间
if (length < capacity)
{
// 直接添加到尾部
data[length] = value;
// 有效元素数量增加
++length;
}
else // 数组已满,需要扩容
{
// 创建新数组,容量翻倍
int* newData = new int[capacity * 2];
// 复制原有数据到新数组
for (int i = 0; i < capacity; i++)
newData[i] = data[i];
// 释放原数组内存
delete[] data;
// 指向新数组
data = newData;
// 更新容量
capacity *= 2;
// 添加新元素
data[length] = value;
++length;
}
}
/**
* 删除指定位置的元素
* @param index 待删除元素的索引
*/
void IntArray::removeAt(int index)
{
// 检查索引合法性
if (index < 0 || index >= length)
{
cout << "越界了" << endl;
return;
}
// 将后续元素前移覆盖被删除元素
for (int i = index; i < length - 1; i++)
{
data[i] = data[i + 1];
}
// 有效元素数量减少
--length;
}
/**
* 删除第一个值等于指定值的元素
* @param num 待删除的值
*/
void IntArray::remove(int num)
{
// 遍历查找指定值
for (int i = 0; i < length; i++)
{
// 找到后删除该位置元素并返回
if (data[i] == num)
{
removeAt(i);
return;
}
}
}
Postion.h
#pragma once
class Postion
{
public:
int x;
int y;
Postion(int x, int y) :x(x), y(y)
{
}
bool operator==(const Postion& pos) const;
bool operator!=(const Postion& pos) const;
};
Postion.cpp
#include "Postion.h"
bool Postion::operator==(const Postion& pos) const
{
if (this->x == pos.x &&
this->y == pos.y)
return true;
return false;
}
bool Postion::operator!=(const Postion& pos) const
{
if (this->x != pos.x ||
this->y != pos.y)
return true;
return false;
}
Vector3.h
#pragma once
class Vector3
{
public:
int x;
int y;
int z;
Vector3(int x, int y, int z) :x(x), y(y), z(z) {}
Vector3 operator+(const Vector3& other) const;
Vector3 operator-(const Vector3& other) const;
Vector3 operator*(const int& num) const;
};
Vector3.cpp
#include "Vector3.h"
Vector3 Vector3::operator+(const Vector3& other) const
{
return Vector3(this->x + other.x, this->y + other.y, this->z + other.z);
}
Vector3 Vector3::operator-(const Vector3& other) const
{
return Vector3(this->x - other.x, this->y - other.y, this->z - other.z);
}
Vector3 Vector3::operator*(const int& num) const
{
return Vector3(this->x * num, this->y * num, this->z * num);
}
Vector3 operator*(const int& num, const Vector3& pos)
{
return Vector3(pos.x * num, pos.y * num, pos.z * num);
}
Lesson16_练习题.cpp
#include <iostream>
#include "Postion.h"
#include "Vector3.h"
#include "IntArray.h"
using namespace std;
Vector3 operator*(const int& num, const Vector3& pos);
int main()
{
#pragma region 练习题一
/*定义一个位置结构体或类,为其重载判断是否相等的运算符
(x1, y1) == (x2, y2) = > 两个值同时相等才为true*/
Postion p = Postion(10, 10);
Postion p2 = Postion(10, 10);
if (p == p2)
cout << "相等" << endl;
else
cout << "不相等" << endl;
if (p != p2)
cout << "不相等" << endl;
else
cout << "相等" << endl;
#pragma endregion
#pragma region 练习题二
/*定义一个Vector3类(x, y, z)通过重载运算符实现以下运算
(x1, y1, z1) + (x2, y2, z2) = (x1 + x2, y1 + y2, z1 + z2)
(x1, y1, z1) - (x2, y2, z2) = (x1 - x2, y1 - y2, z1 - z2)
(x1, y1, z1) * num = (x1 * num, y1 * num, z1 * num)*/
// 创建三维向量对象 v(1,1,1) 和 v2(2,2,2)
Vector3 v = Vector3(1, 1, 1);
Vector3 v2 = Vector3(2, 2, 2);
// 向量加法:v + v2(对应分量相加)
Vector3 v3 = v + v2;
cout << v3.x << "," << v3.y << "," << v3.z << endl; // 输出: 3,3,3
// 向量减法:v - v2(对应分量相减)
Vector3 v4 = v - v2;
cout << v4.x << "," << v4.y << "," << v4.z << endl; // 输出: -1,-1,-1
// 向量数乘(向量在前):v4 * 10(每个分量乘以10)
Vector3 v5 = v4 * 10;
cout << v5.x << "," << v5.y << "," << v5.z << endl; // 输出: -10,-10,-10
// 向量数乘(标量在前):10 * v4(通过非成员运算符重载实现)
v5 = 10 * v4;
cout << v5.x << "," << v5.y << "," << v5.z << endl; // 输出: -10,-10,-10
#pragma endregion
#pragma region 练习题三
/*自定义一个整形数组类,该类中有一个整形数组变量
为它封装增删查改的方法*/
// 封装的好处:无需关心数组容量限制,可自动扩容
IntArray array = IntArray(); // 创建初始容量为10的数组
// 向数组尾部添加元素(共7次添加)
array.addValue(100); // 添加100,当前数组:[100]
array.addValue(200); // 添加200,当前数组:[100, 200]
array.addValue(300); // 添加300,当前数组:[100, 200, 300]
array.addValue(400); // 添加400,当前数组:[100, 200, 300, 400]
array.addValue(500); // 添加500,当前数组:[100, 200, 300, 400, 500]
array.addValue(600); // 添加600,当前数组:[100, 200, 300, 400, 500, 600]
array.addValue(500); // 添加500,当前数组:[100, 200, 300, 400, 500, 600, 500]
// 遍历打印所有元素
for (int i = 0; i < array.getLength(); i++)
{
cout << array[i] << endl; // 输出顺序:100 200 300 400 500 600 500
}
// 删除索引2的元素(值为300)
array.removeAt(2); // 数组变为:[100, 200, 400, 500, 600, 500]
// 再次遍历打印
for (int i = 0; i < array.getLength(); i++)
{
cout << array[i] << endl; // 输出顺序:100 200 400 500 600 500
}
// 删除第一个值为500的元素(索引3的值为500)
array.remove(500); // 数组变为:[100, 200, 400, 600, 500]
// 再次遍历打印
for (int i = 0; i < array.getLength(); i++)
{
cout << array[i] << endl; // 输出顺序:100 200 400 600 500
}
// 将索引1的值修改为888
array.setValue(1, 888); // 数组变为:[100, 888, 400, 600, 500]
// 最终遍历打印
for (int i = 0; i < array.getLength(); i++)
{
cout << array[i] << endl; // 输出顺序:100 888 400 600 500
}
#pragma endregion
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com