✅<1>主页:我的代码爱吃辣
📃<2>知识讲解:C++
🔥<3>创作者:我的代码爱吃辣
☂️<4>开发环境:Visual Studio 2022
💬<5>前言:C++类中一共有六个默认成员函数,今天我们先来将剩下的赋值重载,和取地址重载,以及实现Date类。
目录
一.运算符重载
二.赋值运算符重载格式:
三.前置++和后置++重载
四.const成员
五.取地址及const取地址操作符重载
六. cout 输出类信息
七.Date类实现
(1)Date类的主要功能模块
(2)日期大小比较
(3)日期+天数,日期+=天数,++日期,日期++
(4)日期-日期,日期-天数,日期-=天数
测试
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其
返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)。
假如我们需要实现一个日期加上一个天数得到一个新的日期,此时按照我们以前的思考方式,是实现一个函数,但是利用运算符重载,会变得更加直观明了
class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}void show(){cout << _year << "/" << _month << "/" << _day << endl;}int _year;int _month;int _day;
};
//函数判断日期相等
bool DateEqual(const Date& d1, const Date& d2)
{return d1._year == d2._year &&d1._month == d2._month &&d1._day == d2._day;
}
//运算符重载的定义 使用关键字 operator 【运算符】
bool operator==(const Date& d1 ,const Date& d2)
{return d1._year == d2._year &&d1._month == d2._month && d1._day == d2._day;
}
int main()
{Date d2(2022, 4, 23);Date d1(2022, 1, 13);//函数使用if (DateEqual(d1, d2))cout << "true" << endl;elsecout << "false" << endl;//运算符重载的使用if (d1 == d2)cout << "true" << endl;elsecout << "false" << endl;return 0;
}
但是一般我们的成员变量都是私有的,如果是私有的我们就不能直接访问,我们通过添加 get函数来解决,或者通过将运算符重载的函数写道类的里面,变成成员函数,这里我们就直接封装到类里面:
class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}// bool operator==(Date* this, const Date& date)// 这里需要注意的是,左操作数是this,指向调用函数的对象bool operator ==(const Date& date){return _year == date._year &&_month == date._month && _day == date._day;}void show(){cout << _year << "/" << _month << "/" << _day << endl;}int _year;int _month;int _day;
};
int main()
{Date d2(2022, 1, 13);Date d1(2022, 1, 13);//运算符重载的使用if (d1 == d2)cout << "true" << endl;elsecout << "false" << endl;return 0;
}
注意:在将运算符重载写成成员成员函数时,重载运算符只需要写一个参数即可,因为有一个参数就是本对象本身,就是隐藏在成员函数列表的this指针。
1.参数类型:const T&,传递引用可以提高传参效率
2.返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值检测是否自己给自己赋值
3.返回*this :要复合连续赋值的含义
class Date
{
public:Date(int year = 1900, 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;}//赋值运算符重载Date& operator=(const Date& d){//如果不是自身赋值,即d1=d1,才需要成员变量对用赋值。if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}void show(){cout << _year << "/" << _month << "/" << _day << endl;}
private:int _year;int _month;int _day;
};
int main()
{Date date1;Date date2(2023, 2, 7);date1.show();//因为有返回值就可以实现链式访问(date1 = date2).show();return 0;
}
4. 赋值运算符只能重载成类的成员函数不能重载成全局函数
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;
};
//赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
Date& operator=(Date& left, const Date& right)
{if (&left != &right){left._year = right._year;left._month = right._month;left._day = right._day;}return left;
}
原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现
一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值
运算符重载只能是类的成员函数。
5.用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。
class Time
{
public:Time(){_hour = 1;_minute = 1;_second = 1;}Time& operator=(const Time& t){if (this != &t){cout << "Time:operator=" << endl;_hour = t._hour;_minute = t._minute;_second = t._second;}return *this;}private:int _hour;int _minute;int _second;
};
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void show(){cout << _year << "/" << _month << "/" << _day << endl;}
private:// 基本类型(内置类型)int _year ;int _month;int _day;// 自定义类型Time _t;
};
int main()
{Date d1;d1.show();Date d2(2023,2,7);d1 = d2;d1.show();return 0;
}
注意:内置类型成员变量是直接赋值的,而自定义类型成员变.量需要调用对应类的赋值运算符
重载完成赋。
但是对于有空间申请的空间是需要,我们不能光靠编译器自己生成的赋值运算符重载的:
class A
{
public:A(){a = 10;arr = (int*)malloc(sizeof(int) * a);for (int i = 0; i < a; i++){arr[i] = i;}}void show(){for (int i = 0; i < a; i++){cout << arr[i] << " ";}cout << endl;}int* arr;int a;
};
int main()
{A a;A b;a.show();b.show();b = a;//修改b对象的arr数组for (int i = 0; i < b.a; i++){b.arr[i] = 0;}//观察两个对象中arr的元素b.show();a.show();return 0;
}
我们只是修改了b对象的 arr 数组,但是a对象的arr数组也被跟着修改了。
具体的情况是:
我们知道前置++和后置++唯一的区别就是,返回值不同。前置++返回++之后的结果,前置++返回++之后的结果。那么运算符重载以后也应该支持前置++和后置++的特性。
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}// 前置++:返回+1之后的结果// 注意:this指向的对象函数结束后不会销毁,故以引用方式返回提高效率Date& operator++(){_day += 1;return *this;}// 后置++:// 前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载// C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递// 注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this + 1// 而temp是临时对象,因此只能以值的方式返回,不能返回引用Date operator++(int){Date temp(*this);_day += 1;return temp;}void show(){cout << _year << "/" << _month << "/" << _day << endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2022, 10, 1);d1.show();Date d2 = d1++;d2.show();d1.show();d2 = ++d1;d1.show();d2.show();return 0;
}
将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数
隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << "Print()" << endl;cout << "year:" << _year << endl;cout << "month:" << _month << endl;cout << "day:" << _day << endl << endl;}void ConstPrint() const{cout << "Print()const" << endl;cout << "year:" << _year << endl;cout << "month:" << _month << endl;cout << "day:" << _day << endl << endl;}
private:int _year; // 年int _month; // 月int _day; // 日
};
int main()
{Date d1(2022, 1, 13);d1.Print();const Date d2(2022, 1, 13);d2.ConstPrint();return 0;
}
思考下面的几个问题:
1. const对象可以调用非const成员函数吗?解答:const对象不可以调用非const成员函数,因为非const成员函数的*this也是没有经过const修饰的,如果调用了const成员函数,就相当于权限放大。
2. 非const对象可以调用const成员函数吗?解答:非const对象可以调用const成员函数,因为由const修饰的成员函数实际上修饰的就是*this,相当于权限缩小,是允许的。
3. const成员函数内可以调用其它的非const成员函数吗?解答:const成员函数不可以调用其他的非const成员函数,也是一个权限放大的例子。
4. 非const成员函数内可以调用其它的const成员函数吗?解答:非const成员函数可以调用其他的const成员函数,也是一个权限缩小的例子。
这两个默认成员函数一般不用重新定义 ,编译器默认会生成。
class Date
{
public:Date* operator&(){return this;}const Date* operator&()const{return this;}
private:int _year; // 年int _month; // 月int _day; // 日
};
int main()
{Date date;cout << &date << endl;return 0;
}
这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需
要重载,比如想让别人获取到指定的内容!
class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}Date* operator&(){return (Date*)0x00000001;}const Date* operator&()const{return (Date*)0x0000250;}
private:int _year; // 年int _month; // 月int _day; // 日
};int main()
{const Date date(2022,10,1);Date date1(date);cout << &date << endl;cout << &date1 << endl;return 0;
}
cout是类 ostream 的对象,表示面向窄字符(字符类型)的标准输出流。它对应于 C 流标准输出。
作为 ostream 类的对象,可以使用插入运算符(运算符<<)将字符作为格式化数据写入其中,也可以使用成员函数(如 write)将字符作为无格式数据写入其中。
对象在带有外部链接和静态持续时间的头文件声明
我们可以将ostream的对象cout,也作为运算符重载的参数:
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//<<运算符重载void operator<<(ostream& out){out << _year << "/" << _month << "/" << _day << endl;}
private:int _year;int _month;int _day;
};int main()
{Date date(2023, 2, 8);//按照运算符左边到右边是参数从左往右对应//成员函数第一个参数就是this指针,所以<<运算符左边就是date对象date << cout;//等价于date.operator<<(cout);return 0;
}
但是 date< 这就又有一个新的问题,成员变量私有化,不允许外部函数直接访问,面对这个问题,我们有两种解决方案: 1.设置get函数 设计成员函数用于返回私有成员变量的值,供外部读取。 2.设计友元函数 只需在将函数声明写在Date类的内部,并在函数的前面加上关键字 friend,就可以和类建立友好关系,友元函数可以像成员函数一样随意访问成员变量。 前面一直在用Date类举例子,这次我们来全面实现一下,练习我们之前学过的类与对象的姿势。 我们严格按照开发标准来实现Date类: class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;
};
//<<运算符重载
void operator<<(ostream& out, Date& d)
{out << d._year << "/" << d._month << "/" << d._day << endl;
}
int main()
{Date date(2023, 2, 8);//按照运算符左右参数,分别对应<<运算符重载从左往右的参数cout << date;//等价于operator<<(cout, date);return 0;
}
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//Get函数,用于外部读取私有成员变量int& Get_year(){return _year;}int& Get_month(){return _month;}int& Get_day(){return _day;}
private:int _year;int _month;int _day;
};
//<<运算符重载,为了支持连续访问,加上返回值
otsream& operator<<(ostream& out, Date& d)
{out << d.Get_year() << "/" << d.Get_month() << "/" << d.Get_day() << endl;return out;
}
int main()
{Date date(2023, 2, 8);//按照运算符左右参数,分别对应<<运算符重载从左往右的参数cout << date;//等价于operator<<(cout, date);return 0;
}
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//友元函数friend void operator<<(ostream& out, Date& d);
private:int _year;int _month;int _day;
};
//<<运算符重载
void operator<<(ostream& out, Date& d)
{out << d._year << "/" << d._month << "/" << d._day << endl;
}
int main()
{Date date(2023, 2, 8);//按照运算符左右参数,分别对应<<运算符重载从左往右的参数cout << date;//等价于operator<<(cout, date);return 0;
}
七.Date类实现
(1)Date类的主要功能模块
//Date.h文件
#include
(2)日期大小比较
//Date.cpp
bool Date::operator>(const Date& date)
{if (_year != date._year)//如果年不相等,直接判断出结果return _year > date._year;if (_month != date._month)//年相等的时候,如果月不相等,直接判断出结果return _month > date._month;if (_day != date._day)//年.月相等的时候,如果日不相等,直接判断出结果return _day > date._day;
}bool Date::operator >= (const Date& date)
{//>=就是或上一个相等return *this > date || *this == date;
}bool Date::operator ==(const Date& date)
{ //相等需要年月日都相等return _year == date._year && _month == date._month && _day == date._day;
}bool Date::operator <(const Date& date)
{ // >=取反return !(*this >= date);
}bool Date::operator<=(const Date& date)
{ //<或上==return *this < date || *this == date;
}
(3)日期+天数,日期+=天数,++日期,日期++
int Date::GetMonthDay(int year, int month)
{int montharr[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };if (((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)&&month==2){return 29;}return montharr[month];
}Date Date::operator+(int x)
{Date tmp(*this);if (x < 0){tmp -= -x;return tmp;}tmp += x;return tmp;
}
//先将天数全部加上去,然后依次减去当月天数,并判断年月的变换。
Date& Date::operator+=(int x)
{if (x < 0){*this -= -x;return *this;}_day += x;while (_day > GetMonthDay(_year,_month)){_day -= this->GetMonthDay(_year,_month);_month++;if (_month > 12){_year++;_month = 1;}}return *this;
}//Date++(后置++)
Date Date::operator++(int)
{Date temp(*this);*this+=1;return temp;
}
//++Date(前置++)
Date& Date::operator++()
{*this += 1;return *this;
}
(4)日期-日期,日期-天数,日期-=天数
//计数计算,一天一天加,直到日期相等。
int Date::operator-(const Date& date)
{int count = 0;Date bigdate = *this;Date smalldate = date;if (bigdate < smalldate){Date tmp = bigdate;bigdate = smalldate;smalldate = tmp;}while (!(smalldate == bigdate)){smalldate++;count++;}return count;
}//日期-天数,先算整数月的减法,直到剩下的天数,小于本月天数,
//加上第一个月,和最后一个月的剩下的天数计算
Date Date::operator-(int x)
{Date tmp(*this);if (x < 0)//如果天数小于0,就相当于加上天数{tmp += -x;return tmp;}tmp._month--;if (tmp._month == 0){tmp._month = 12;tmp._year--;}tmp._day = tmp.GetMonthDay(tmp._year, tmp._month);while (x > tmp.GetMonthDay(tmp._year, tmp._month)){x -= tmp.GetMonthDay(tmp._year, tmp._month);tmp._month--;if (tmp._month == 0){tmp._month = 12;tmp._year--;}tmp._day = tmp.GetMonthDay(tmp._year, tmp._month);}tmp._day -= x;return tmp + this->_day;
}
//不仅计算返回,而且修改当前对象
Date Date::operator-=(int x)
{if (x < 0){*this += -x;return *this;}*this = *this - x;return *this;
}
测试
上一篇:“穿搭速递”首页制作
下一篇:[多线程进阶] 常见锁策略