百度认证号码平台,青岛做优化网站哪家好,网站建设设计制作包头,未来10大暴利行业1 运算符重载
运算符重载是一种特殊的函数#xff0c;通过operator关键字定义#xff0c;让自定义类型支持标准运算符#xff1a;
Vector operator(const Vector v1, const Vector v2) {return Vector(v1.x v2.x, v1.y v2.y);
}
基本语法规则#xff1a;
返…1 运算符重载
运算符重载是一种特殊的函数通过operator关键字定义让自定义类型支持标准运算符
Vector operator(const Vector v1, const Vector v2) {return Vector(v1.x v2.x, v1.y v2.y);
}
基本语法规则
返回类型 operator运算符符号(参数列表) {// 实现逻辑
}
参数是操作符的操作个数如比较大小使用双操作符解引用、取地址、、--使用单操作符。
eg判断是否相等
class Date
{
public:Date(int year 1900, int month 1, int day 1){_year year;_month month;_day day;}int GetYear(){return _year;}//private:int _year;int _month;int _day;
};bool operator(const Date d1, const Date d2)//判断是否相等
{return d1._year d2._year d1._month d2._month d1._day d2._day;
}int main()
{Date d3(2024, 4, 14);Date d4(2024, 4, 14);// 显式调用operator(d3, d4);if (d3 d4)// 直接写转换调用编译会转换成operator(d3, d4);{cout 相等 endl;}else{cout 不相等 endl;}return 0;
}
在上述函数中将运算符重载成全局为了访问私有成员将private改为public对程序的封装性不友好。
解决方案: 1、提供这些成员get和set 2、友元 3、重载为成员函数(一般使用这种)
以重载为成员函数为例
class Date
{
private:int _year;int _month;int _day;
public:Date(int year 1900, int month 1, int day 1){_year year;_month month;_day day;}//bool operator(Date* this, const Date d)成员函数都隐藏this指向调用函数的对象bool operator(const Date d){return this-_year d._year this-_month d._month this-_day d._day;}
};
int main()
{Date d3(2024, 4, 14);Date d4(2024, 4, 14);// 显式调用d3.operator(d4);//》bool operator(Date* d3,const Date d4)// 直接写装换调用编译会转换成operator(d3, d4);if (d3 d4){cout 相等 endl;}else{cout 不相等 endl;}return 0;
}
注意由于成员函数中都存在一个指向调用函数的对象的this,所以在重载为成员函数时应从bool operator(Date d1, const Date d2) 变为bool operator(Date* this, const Date d)zbool operator(const Date d)
1.1 运算符重载原则
不能创建新运算符只能重载C已有的运算符即函数名必须是C/C中有的。
// 非法operator 不存在
至少一个类类型操作数
// 合法操作数包含自定义类型
Vector operator(const Vector, const Vector);// 非法两个内置类型
int operator(int, int);
不能改变运算符的原始语义用于内置类型的运算符其含义不能改变例如内置的整形不能改变其含义。
不可重载的运算符.*成员指针访问、::作用域解析、?:三元条件sizeof、.成员访问。
成员指针访问不常见这里举一个例子。
class OB
{
public:void func(){cout void func() endl;}
};typedef void(OB::* PtrFunc)();//将PtrFunc设置为成员函数指针类型int main()
{//普通函数使用函数名就是地址成员函数规定要加PtrFunc ptr OB::func;//OB temp;(temp.*ptr)();//使用.*解引用func函数return 0;
}
2 赋值运算符重载
赋值运算符用于将已有对象的值复制给另一个已存在的对象。与拷贝构造函数不同它作用于已初始化的对象。
拷贝构造一个已经存在的对象拷贝给另一个创建同时初始化的对象。赋值针对两个已经存在的对象class A和classB,将A赋值给B或将B赋值给A。
Date d1(2023, 1, 1);
Date d2(2024, 2, 2);
d1 d2; // d1和d2在前两行已经存在调用赋值运算符
Date d3(d1);//d3在这行刚刚创建用d1初始化d3调用拷贝构造
当用户没有显式实现时编译器会生成一个默认赋值运算符重载内置类型成员变量直接赋值自定义类型需调用对应类的赋值运算符完成赋值。
2.1赋值运算符重载格式
class Date {
public:// 赋值运算符重载Date operator(const Date other) {if(this ! other) { // 防止自赋值_year other._year;_month other._month;_day other._day;}return *this; // 支持链式赋值}
};
关键设计要素 返回类型Date支持连续赋值 a b c若返回值为Datereturn *this会生成一个当前对象的临时拷贝作为当前函数返回值自定义类型需要调用拷贝构造使用Date引用返回返回一个别名不需要拷贝构造提高了运行效率。 参数类型const Date传递引用可以提高传参效率避免拷贝同时保护原对象。自赋值检查if(this ! other)防止自己给自己复制浪费计算资源。返回当前对象return *this规定返回左操作数即调用函数的类》隐藏的*this
class Date
{
public:Date(int year 1900, int month 1, int day 1)//默认构造函数{_year year;_month month;_day day;}Date(const Date d)//拷贝构造函数{//清楚观察调用拷贝构造函数cout 调用拷贝构造函数Date(const Date d) endl;_year d._year;_month d._month;_day d._day;}//赋值拷贝// d2this,d3d;Date operator(const Date d){if(this ! d){_year d._year;_month d._month;_day d._day;}return *this;//左操作数作为返回值}void Print(){cout _year - _month - _day endl;}~Date()//析构函数{//清楚观察调用析构函数cout ~Date() endl;_year -1;_month -1;_day -1;}private:int _year;int _month;int _day;
};
int main()
{Date d2(2025, 6, 18);d2.Print();Date d3(2026, 7, 20);d2 d3;d2.Print();d3.Print();
} 上述程序中若赋值函数为void operator(const Date d)完成d2d3赋值操作(此时应删去return *this)具体表现为d2.Print和d3.Print输出结果相同。但由于d2d3》d2.operator(d3)》d2调用operator函数完成赋值后返回值为void此时如果想继续赋值即d1d2d3会发生报错。具体如下 对于int i,j,k. i jk1连续赋值1作为右操作数赋值给k返回值为int k,k作为右操作数赋值给j返回值为int jj作为右操作数赋值给i返回值为int i。在这之中若函数的返回值为void,则会发生报错“没有找到接受void类型的右操作数的运算符”。所以为支持连续赋值函数的参数类型、返回值类型应保持一致。
函数返回值总结
返回对象是一个局部对象或者临时对象出当前函数作用域就析构销毁了就不能使用引用返回如果使用引用返回引用对象在func函数栈上就已经销毁了导致野引用。虽然引用返回可以减少一次拷贝情况但必须是出了函数作用域后返回对象还在才能使用引用返回。赋值重载应注意返回值要支持连续赋值并且用引用返回同时判断一下是否是自己给自己赋值
2.2 赋值运算符显示实现
赋值运算符只能重载成类的成员函数不能重载成全局函数。
class Date
{
public:Date(int year 1900, int month 1, int day 1){_year year;_month month;_day day;}int _year;int _month;int _day;
};Date operator(Date left, const Date right)
{if (left ! right){left._year right._year;left._month right._month;left._day right._day;}return left;
}
在上述程序中赋值运算符如果不能显式实现编译器会生成一个默认的此时用户在类外自己生成一个全局赋值运算符重载就和编译器在类中生成的冲突了故赋值运算符只能重载是类的成员函数。
用户没有显示实现赋值运算符时编译器会生成一个默认赋值运算符并以字节方式逐渐拷贝。内置类型成员变量直接赋值自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值与拷贝构造类似。
3 利用运算符重载实现日期类
头文件
#pragma once
#includeiostream
using namespace std;#includeassert.hclass Date
{
private:int _year;int _month;int _day;//友元函数声明使operator可以调用Date中的private数据friend ostream operator(ostream out, const Date d);friend istream operator(istream in, Date d);//友元
public:Date(int year 1900, int month 1, int day 1);//缺省参数尽量在声明时候给void Print() const;// 直接定义类里面他默认是inline// 频繁调用int GetMonthDay(int year, int month){assert(month 0 month 13);static int monthDayArray[13] { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };// 365天 5h if (month 2 (year % 4 0 year % 100 ! 0) || (year % 400 0)){return 29;}else{return monthDayArray[month];}}bool CheckDate();bool operator(const Date d) const;bool operator(const Date d) const;bool operator(const Date d) const;bool operator(const Date d) const;bool operator(const Date d) const;bool operator!(const Date d) const;// d1 100Date operator(int day);Date operator(int day) const;Date operator-(int day);//d前置Date operator();//d后置Date operator(int);//增加一个int形参构成函数重载//前置--Date operator--();//后置--Date operator--(int);int operator-(const Date d)const;};
其中
Date operator();属于前置为了与后置区分构成函数重载强行增加一个Int(不需要写形参名接受值是多少不重要也不用仅仅是为了跟前置区分开)。friend ostream operator(ostream out, const Date d);和friend istream operator(istream in, Date d);是在类外实现的在类内构成Date类的友元函数可以访问类内的私有元素。bool operator(const Date d) const;中的const是为了修视成员函数中隐藏的this指针表明在该成员函数中不能对类的任何成员进行修改。
具体实现
#define _CRT_SECURE_NO_WARNINGS 1
#include Date.hbool Date::CheckDate()
{if (_month 1 || _month 12|| _day 1 || _day GetMonthDay(_year, _month)){return false;}else{return true;}
}Date::Date(int year, int month, int day)
{_year year;_month month;_day day;if (!CheckDate()){cout 日期非法 endl;}
}void Date::Print() const//声明和定义分离要制定类域定义声明中类的函数返回值类型 类名::函数名()
{cout _year - _month - _day endl;
}bool Date::operator(const Date d) const
{if (this-_year d._year){return true;}else if (this-_year d._year){if (this-_month d._month){return true;}else if (this-_month d._month){return this-_day d._day;}}return false;
}
bool Date::operator(const Date d) const
{if (*this d){return false;}else{return true;}
}bool Date::operator(const Date d) const
{return this-_year d._year this-_month d._month this-_day d._day;
}bool Date::operator(const Date d) const
{if (*this d || *this d){return false;}else{return true;}
}bool Date::operator(const Date d) const
{if (*this d){return false;}else{return true;}
}bool Date::operator!(const Date d) const
{if (*this d){return false;}else{return true;}
}Date Date::operator(int day)
{if (day 0){day -day;return (*this).operator-(day);}_day _day day;while(_day GetMonthDay(_year, _month))//大于当前月天数要进月{//说明现在的天数和大于当前月的最大天数//天数-本月最大天数月进位_day _day - GetMonthDay(_year, _month);//减去当前月的天数_month;//月份进1类似十进制加法。11101*10110 1*10112*10 (11-10);if (_month 12){_month 1;_year;}}return *this;
}Date Date::operator(int day) const
{Date tmp *this;tmp day;return tmp;
}Date Date::operator-(int day)
{if (day 0){day -day;return (*this).operator(day);}_day _day - day;//借当前月份的天数while (_day 0){//说明当前月份天数借完了开始借上个月天数借位_month--;if (_month 0){_month 12;_year--;} _day _day GetMonthDay(_year, _month);}return *this;
}//前置
Date Date::operator()
{*this 1;return *this;
} //后置
Date Date::operator(int)
{Date tmp(*this);*this 1;return tmp;
}
Date Date::operator--()
{*this - 1;return *this;
}
Date Date::operator--(int)
{Date tmp(*this);*this - 1;return tmp;
}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 n 0;while (min ! max){min;n;}return n*flag;
}ostream operator(ostream out, const Date d)
{out d._year 年s d._month 月 d._day 日 endl;return out;
}istream operator(istream in, Date d)
{cout 请依次输入年、月、日 endl;in d._year d._month d._day;if (!d.CheckDate()){cout 日期非法 endl;}return in;
}