【C++】运算符重载

【C++】运算符重载 引言内置类型可以直接用运算符简单并且可以直接转为对应的指令。而自定义类型不可以直接用没有对应的指令。例比较int 简单我们知道21而比较Date 对象单纯用符号我们不知道该怎样进行比较。所以需要我们自行规定怎样去进行比较这就是运算符重载。实质运算符重载的实质是具有特殊名字的函数。格式返回类型 operator运算符 参数列表{函数体}说明同样具有返回类型与运算符操作结果保持一致、参数列表和函数体。参数个数和运算符作用的运算对象数量一样多。例比较Date对象。先将运算符重载函数定义在全局。注意参数列表用 const 引用对象。避免操作失误导致对象被意外改变。避免权限被放大。将运算符重载定义在全局而对于全局函数若要访问的成员变量被类 私有化出现问题。有以下几种解决方案成员放公有提供Getxxx函数Java常用友元函数直接将该函数定义到类里面做成员函数。注意此时参数列表中隐含了 this 作为参数之一。class Date { public: Date(int year 1, int month 1, int day 1) { _year year; _month month; _day day; } void Print() { cout _year 年 _month 月 _day 日 endl; } //重载为成员函数解决访问问题。 //这里只需要再传入一个参数即可默认第一个参数为this bool operator (const Date d1) { return _year d1._year _month d1._month _day d1._day; } private: int _year; int _month; int _day; };调用方式函数调用有两种形式。Date d1(2026, 6, 13); Date d2(2026, 6, 13); //运算符重载的两种调用方式。 d1 d2; //这种比较常用。 d1.operator(d2); //注意优先级这里优先级高于所以加 cout (d1 d2) endl;如何设计要有意义。例1.日期减天数、日期减日期减号-) 重载具有意义。Date Date::operator-(int day) { if (day 0) //用户可能输入负数。调用来实现。 { return *this -day; } _day - day; while (_day 0) { --_month; if (_month 0) { --_year; _month 12; } //先将月份借位了再计算_day. _day GetMonthDay(_year,_month); } return *this; } //但是这种实现逻辑结束后原日期也被更改。不是我们要的结果。 //要让原日期不跟着改变用拷贝构造一个辅助日期来进行操作。 Date Date::operator-(int day)const { Date tmp(*this); //为了拿到当前的调用对象拷贝构造传*this进去。 tmp - day; // 调用- 函数来复用。优化了代码。 return tmp; }//两日期相减 *this-d? 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 count 0; while (max ! min) //代码复用min每次1直到max { //加了多少次就差了多少天。 min; count; } return count * flag; }2.日期相加无意义。但是日期加xx天有意义。故号可以重载。Date Date::operator(int day) { if (day 0) //用户可能输入负数调用-来实现。 { return *this - -day; } _day day; while (_day GetMonthDay(_year,_month)) { _day - GetMonthDay(_year, _month); _month; //使用前置--减少拷贝。 if (_month 13) { _month 1; _year; } } return *this; } Date Date::operator(int day)const { Date tmp(*this); tmp day; return tmp; }实现逻辑如何获取某一月的总天数。统一用GetMonthDay函数实现因为是频繁调用的小函数考虑用内联//Date类成员函数 //调用频繁且规模小考虑设为内联函数声明和定义不可分离。 int GetMonthDay(int year,int month) const { assert(month 0 month 13); //设置为静态数组不用每次调用都开辟新空间创建一样的数组。 //用数组下标表示月份数组对应的值表示该月的总天数。 static int monthDayArray[13] { -1,31,28,31,30,31,30,31,31,30,31,30,31 }; //判断先判断简单的month2效率高一点 if (month 2 ((year % 4 0 year % 100 ! 0) || (year % 400 0))) { return 29; } else { return monthDayArray[month]; } }tips能用引用返回就用引用返回减少拷贝。判断函数结束后返回的值还存不存在存在就能引用返回。注意重载的运算符必须是语法中有的符号不能自己创建新的。重载操作符至少有一个类类型参数。不作用于完全的内置类型间。//编译报错operator 必须至少有一个类类型的形参 int operator(int x, int y) { return x - y; }.* :: sizeof ? . 这5个运算符不可重载。(*可以重载解释(.*) :了解即可成员函数指针成员函数要取地址前面得加 取地址符号。成员函数指针和成员函数在声明时也得指定类域。成员函数指针调用需要用对象进行调用此时 aa.*func)();class A { public: void func() { cout A::func() endl; } }; typedef void(A::* PF)();//成员函数指针类型//C中规定成员函数要加才能取到函数指针 PF pf A::func; A obj;//定义ob类对象temp //对象调用成员函数指针时使用.*运算符 (obj.*pf)();4. 函数重载和运算符重载重载的意义不同。两个运算符重载可以构成函数重载。例如有前置、后置之分。为了区分规定对于后置参数列表增加一个int。与前置构成重载进行区分。但我们并不会真正使用int只是让编译器能够区分出两种自动进行类型匹配 。//类成员函数中定义。 //前置先操作再返回值 Date operator() { _day 1; return *this; } //后置先返回值再操作 Date operator(int) { Date tmp(*this); _day 1; return tmp; }Date d1(2026, 6, 13); Date d2(2026, 6, 13); Date d3 d1; d1.Print(); d3.Print(); Date d4 d2; d2.Print(); d4.Print();前置性能更好因为是引用返回减少了拷贝而后置需要进行2次拷贝。赋值 运算符重载同构造、析构、拷贝构造函数一样也是一个默认的成员函数。与拷贝构造进行区分因为都要用到 号进行操作,容易混淆相同点它们都进行对象的拷贝。不同点拷贝构造核心是用一个已存在的对象去初始化一个需要实例化的对象构造而赋值运算符重载是针对已经存在的两对象之间的拷贝。特点是一个运算符重载但规定必须重载为成员函数。参数建议写成const 引用减少拷贝。要设置返回值才可以连续赋值。若返回值还存在建议用引用返回减少拷贝。无显式实现时编译器会自动生成。与拷贝构造一致对内置类型成员变量完成值拷贝/浅拷贝一个字节一个字节拷贝对自定义类型成员变量调用他的赋值运算符重载。(参考拷贝构造深浅拷贝显式析构、显式拷贝构造、显式赋值运算符重载应该是同时出现的。Date(const Date d) //拷贝构造参数建议写成const引用 { cout Date(const Date d) endl; _year d._day; _month d._month; _day d._day; } Date operator(const Date d) { cout Date operator(const Date d) endl; //用对象的地址检查自己给自己赋值的情况。 if (this ! d) { _year d._day; _month d._month; _day d._day; } //d1 d2表达式的返回对象应该为d1也就是*this return* this; }Date d1(2026, 6, 13); Date d2(d1); //调用拷贝构造将d1拷贝给d3 Date d3 d1; Date d4(2026,6,14); d1 d4; //调用赋值运算符重载流插入/提取 运算符重载是什么依托函数重载可以对不同类型操作打印和输入。C中的print和scanf无论怎样设置都无法对自定义类型操作C提出流插入/提取重载就可以对自定义类型进行操作。说明cout是 ostream类型 的对象。cin是 istream类型 的对象。ostream operator(ostream out, const Date d) { out d._year 年 d._month 月 d._day 日 endl; return out; }istream operator(istream in, Date d) { cout 请依次输入年月日: endl; in d._year d._month d._day; while(!d.CheckDate()) { cout 请重新输入 endl; in d._year d._month d._day; } return in; }注意但凡重载为成员函数就会自动把this置于第一个形参位置。即this在前cout在后。用 coutd ;调用时因与形参的顺序不一致出错。所以不做成员函数而做全局函数。重置形参顺序。但是无法访问到类中私有的成员变量。解决在类中声明为友元函数。全局函数声明和友元函数声明都得来一遍因为它们的域不同。cin也是一样的逻辑。const成员函数为什么要用//成员函数 void Print(); //编译出错Print函数中第一个形参是Date* this //将const Date传入导致权限放大。 void TestDate() { const Date d1(2026, 4, 14); d1.Print(); }解决使用const修饰成员函数形参会被设置为const Date 权限平级了可以使用。格式void Print() const;建议凡是不改变调用对象的函数都建议用const修饰。//非const对象也可以调用const成员函数权限可以缩小 Date d1(2026,6,14); d1.Print(); const Date d2(2026,4,5); d2.Print();取地址运算符重载分为普通取地址运算符重载和const取地址运算符重载。它们构成函数重载。一般这两个函数编译器自动生成的已经够用 了我们不需要显式实现。class Date { public: Date* operator() { return this; //return nullptr; } const Date* operator() const { return this; //return nullptr; } private: int _year; int _month; int _day; };