【C++】005 C++进阶实战:从.h到.cpp,手把手带你吃透运算符重载(附完整代码)
2026/9/24 17:28:27 网站建设 项目流程

好,我们书接上回,继续讲解C++类和对象(中)的相关内容。

5.2 赋值运算符重载

赋值运算符有一个默认成员函数,用于完成两个已经存在的对象直接的拷贝赋值,这里要注意跟拷贝构造区分,拷贝构造用于一个对象拷贝初始化给另一个要创建的对象。

赋值运算符重载的特点:

1. 赋值运算符重载是一个运算符重载,规定必须重载为成员函数。赋值运算符重载的参数建议写成const当前类类型引用,否则会传值传参会有拷贝。

#include<iostream> using namespace std; class Date { public: Date(int year = 1, int month = 1, int day = 1) { _year = year; _month = month; _day = day; } Date(const Date& d) { _year = d._year; _month = d._month; _day = d._day; } void operator=(const Date d) { _year = d._year; _month = d._month; _day = d._day; } void Print() { cout << _year << "-" << _month << "-" << _day << endl; } private: int _year; int _month; int _day; }; int main() { Date d1(2026, 9, 18); Date d2(d1); Date d3(2026, 9, 18); d1 = d3; //属于赋值运算符重载 Date d4 = d1;//属于拷贝构造 return 0; }

2. 有返回值,且建议写成当前类类型引用,引用返回可以提高效率,有返回值目的是为了支持连续赋值场景。

Date& operator=(const Date d) { //此处是为了防止自己的值赋给自己 if(this!=&d) { _year = d._year; _month = d._month; _day = d._day; } return *this; } }; int main() { Date d1(2026, 9, 18); Date d2(d1); Date d3(2026, 9, 18); d1 = d3; Date d4 = d1; Date d5(2026, 10, 1); //连续赋值 d1 = d3 = d5; d1.Print(); return 0; }

3. 没有显式实现时,编译器会自动生成一个默认运算符重载,默认赋值运算符重载行为跟默认拷贝构造函数类似,对内置类型成员变量会完成值拷贝/浅拷贝(一个字节一个字节的拷贝),对自定义类型成员变量会调用它的赋值重载函数。

4. 像Date这样的类成员变量全是内置类型且没有指向什么资源, 编译器自动生成的赋值运算符重载就可以完成需要的拷贝,所以不需要我们显式实现赋值运算符重载。像Stack这样的类,虽然也都是内置类型,但是_a指向了资源,编译器自动生成的赋值运算符重载完成的值拷贝/浅拷贝不符合我们的需求,所以需要我们自己实现深拷贝(对指向的资源也进行拷贝)像MyQueue这样的类型内部主要是自定义类型Stack成员,编译器自动生成的赋值运算符重载会调用Stack的赋值运算符重载,也不需要我们显式实现MyQueue的赋值运算符重载。这里还有一个小技巧如果一个类显式实现了析构并释放资源,那么它就需要显式写赋值运算符重载,否则就不需要


六. 综合练习

我们现在借助日期类来实现运算符的重载,本次练习我们以下面几个运算符的重载为例,它们分别是:+=、+、前置++、后置++、-=、-、前置--、后置--等等。

6.1 .h文件

#pragma once #include<iostream> #include<assert.h> using namespace std; class Date { public: Date(int year = 1900, int month = 1, int day = 1); void Print(); 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 }; if (month == 2 && (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { return 29; } else { return monthDayArray[month]; } } bool CheckDate() { if (_month < 1 || _month>12) return false; if (_day<1 || _day>GetMonthDay(_year, _month)) return false; return true; } bool operator<(const Date& d); bool operator<=(const Date& d); bool operator>(const Date& d); bool operator>=(const Date& d); bool operator==(const Date& d); bool operator!=(const Date& d); //d1+=天数 Date& operator += (int day); Date operator+(int day); //d1-=天数 Date& operator-=(int day); Date operator-(int day); //d1-d2 int operator-(const Date& d); Date& operator++(); //d1++ -> d1.operator++(0) //为了区分,构成重载,给后置++,强行增加一个int形参 //这里不需要写形参名,因为接收值是多少不重要,也不需要用 //这个参数仅仅是为了跟前置++构成重载区分 Date operator++(int); //前置 Date& operator--(); //后置 Date operator--(int); //友元函数的声明 friend ostream& operator<<(ostream& out, const Date& d); friend istream& operator>>(istream& in, Date& d); private: int _year; int _month; int _day; }; //<< ostream& operator<<(ostream& out, const Date& d); istream& operator>>(istream& in, Date& d);

6.2 .cpp文件

//综合练习 #include"lesson3.h" Date::Date(int year, int month, int day )//注意缺省参数声明和定义不能同时给 { _year = year; _month = month; _day = day; if (!CheckDate()) { cout << "非法日期:>" << *this; } } void Date::Print() { cout << _year << "-" << _month << "-" << _day<<endl; } //d1+=100 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) { ++_year; _month = 1; } } //上述代码可以替换成*this=*this+day; return *this; } //d1+100 Date Date::operator+(int day) { Date tmp(*this);//拷贝构造,*this是d1,d1不能改变 tmp._day += day; while (tmp._day > GetMonthDay(_year, _month)) { tmp._day -= GetMonthDay(_year, _month); ++_month; if (tmp._month == 13) { ++tmp._year; tmp._month = 1; } }//以上内容可替换成tmp+=day return tmp; } //d5++ -> d5.operator++(0) Date Date::operator++(int) { Date tmp(*this); *this += 1; return tmp; } //++d6 Date& Date:: operator++() { *this += 1; return *this; } //-= Date& Date::operator-=(int day) { if (day < 0) { return *this += -day; } _day -= day; while (_day <= 0) { --_month; if (_month == 0) { --_year; _month = 12; } _day += GetMonthDay(_year, _month); } return *this;//出了作用域d1还在,所以用引用返回可以减少拷贝 } //- Date Date::operator-(int day) { Date tmp = *this; tmp -= day; return tmp; } //前置--和后置-- Date& Date::operator--() { *this -= 1; return *this; } Date Date::operator--(int) { Date tmp = *this; *this -= 1; return tmp; } bool Date::operator<(const Date& d) { if (_year < d._year) return true; else if (_year == d._year) { if (_month < d._month) return true; else if (_month == d._month) return _day < d._day; } return false; } bool Date::operator<=(const Date& d) { return *this < d || *this == d; } bool Date::operator>(const Date& d) { if (_year > d._year) return true; else if (_year == d._year) { if (_month > d._month) return true; else if (_month == d._month) return _day > d._day; } return false; //上述代码也可以写成:return !(*this<=d); } bool Date::operator>=(const Date& d) { return !(*this < d); } bool Date::operator==(const Date& d) { return _year == d._year && _month == d._month && _day && d._day; } bool Date::operator!=(const Date& d) { return !(*this == d); } //日期-日期 int Date::operator-(const Date& d) { Date max = *this; Date min = d; int flag = 1; if (*this < d) { min = *this; max = d; flag =-1; } int day = 0; while (min != max) { ++min; ++day; } return day * flag; } //<< ostream& operator<<(ostream& out,const Date& d) { out << d._year << "/" << d._month << "/" <<d._day << endl; return out; } //>> istream& operator>>(istream& in, Date& d) { while (1) { cout << "请依次输入年月日:>" << endl; in >> d._year >> d._month >> d._day; if (d.CheckDate()) { break; } else { cout << "输入日期非法,请重新输入:" << endl; } } return in; } void test1() { Date d1(2025, 8, 1); Date d2 = d1 += 100; d1.Print(); d2.Print(); Date d3(2025, 8, 1); Date d4 = d3 + 100; d3.Print(); d4.Print(); Date d5(2025, 8, 1); Date rat1 = d5++; rat1.Print(); Date d6(2025, 8, 1); Date rate2 = ++d6; rate2.Print(); //Date d7=operator+(100); Date d7 = d1.operator+(100); Date d8 = d1 + 100; Date d9(2025, 9, 8); d9 -= 50; d9.Print(); Date d10 = d9 - 50; } void test2() { Date d1(2025, 9, 8); Date d2=--d1; d1.Print(); d2.Print(); Date d3 = d1--; d1.Print(); d3.Print(); } void test3() { Date d1(2026, 9, 21); Date d2(2026, 10, 1); cout << d2 - d1<<endl; } void test4() { Date d1(2026, 9, 21); Date d2(2026, 10, 1); //operator<<(cout, d1); cin >> d1 >> d2; cout << d1 << d2; //虽然可以跑,但是不符合可读性 /*d1.operator<<(cout); d1 << cout;*/ } int main() { //test1(); /*test2(); test3();*/ test4(); int i = 1; double d = 1.1; cout << i;//cout.operator(i) cout << d;//cout.operator(d) return 0; }

所以图片中两种代码复用方式,上面的方式要优于下面的代码复用方式,因为上面一次总共调用两次拷贝,而下面要调用五次拷贝。所以建议让拷贝多的代码块去复用拷贝少的代码块


七. 取地址运算符重载

7.1 const成员函数

1. 将const修饰的成员函数称之为const成员函数,const修饰成员函数放到成员函数参数列表的后面。

2. const实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。const修饰Date类的Print成员函数,Print隐含的this指针由Date* const this变为const Date* const this。(总结:不修改成员变量的成员函数都应该加上const。优势是普通对象和const对象都可以调用const成员函数

#include<iostream> using namespace std; class Date { public: Date(int year = 1, int month = 1, int day = 1) { _year = year; _month = month; _day = day; } //const成员函数 //void Print(const Date* const this) const void Print()const //表示const修饰的是this指针,此时this指针不能改变,它指向的内容也不能改变 { cout << _year << "-" << _month << "-" << _day << endl; } private: int _year; int _month; int _day; }; void test1() { const Date d1(2026, 9, 23); d1.Print(); Date d2(2026, 9, 23); d2.Print(); } int main() { test1(); return 0; }

7.2 取地址运算符重载

取地址运算符重载分为普通取地址运算符重载和const取地址运算符重载,一般这两个函数编译器自动生成的就够我们用了,不需要去显式实现。除非一些很特殊的场景,比如我们不想让别人取到当前类对象的地址,就可以自己实现一份,胡乱返回一个地址。

#include<iostream> using namespace std; class Date { public: Date(int year = 1, int month = 1, int day = 1) { _year = year; _month = month; _day = day; } //const成员函数 //void Print(const Date* const this) const void Print()const //表示const修饰的是this指针,此时this指针不能改变,它指向的内容也不能改变 { cout << _year << "-" << _month << "-" << _day << endl; } Date* operator&() { return this; //return nullptr; } const Date* operator&()const { return this; //return nullptr; } private: int _year; int _month; int _day; }; void test1() { const Date d1(2026, 9, 23); d1.Print(); Date d2(2026, 9, 23); d2.Print(); Date* p1 = &d2; const Date* p2 = &d1; cout << p1 << " " << p2 << endl; } int main() { test1(); return 0; }

以上就是我们今天的内容,至此,类和对象(中)已完结~ 下一节,我将讲述“类和对象(下)”,大家敬请期待。

觉得有帮助的朋友们可以用一键三连喔~互三必回!有不足之处欢迎大家评论区批评指正,后续内容会更加精彩!

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询