C++的类与对象(五):赋值运算符重载与日期类的实现
目录
比较两个日期对象
运算符重载
赋值运算符重载
连续赋值
日期类的实现
Date.h文件
Date.cpp文件
Test.cpp文件
const成员
取地址及const取地址操作符重载
比较两个日期对象
问题描述:内置类型可直接用运算符比较,自定义类型的对象是多个内置类型的集合编译器不支持
解决办法: 自定义比较函数
#include <iostream>
using namespace std;class Time
{
public:Time(int year, int month,int day){_year = year;_month = month;_day = day;}
//private:int _year;int _month;int _day;
};//年份是否相等
bool TimeCompare1(const Time&x,const Time& y)
{return x._year == y._year&& x._month == y._month&& x._day == y._day;
}//左年份是否小于右年份
bool TimeCompare2(const Time& x, const Time& y)
{if (x._year < y._year){return true;}else if (x._year == y._year){if (x._month < y._month){return true;}else if (x._month == y._month){return x._day < y._day;}}return false;
}int main()
{Time t1(2024, 1, 28);Time t2(2024, 2, 27);cout << TimeCompare1(t1, t2) << endl;cout << TimeCompare2(t1, t2) << endl;return 0;
}
代码的编写者知道TimeCompare1和TimeCompare2两个函数的区别,但是对于其他人来说只能大概的知道它们是两个比较函数?对此C++提出了运算符重载的概念帮助帮助我们更好的了解这些比较函数的大致功能
运算符重载
格式:返回类型 operator 操作符(参数列表)
作用:不仅解决了函数名不规范的问题增强代码的可读性,而且还使自定义类型也可以用运算符
注意事项:
1、operator关键字+运算符做函数名
#include <iostream>
using namespace std;class Time
{
public:Time(int year, int month,int day){_year = year;_month = month;_day = day;}
//private:int _year;int _month;int _day;
};bool operator==(const Time&x,const Time& y)
{return x._year == y._year&& x._month == y._month&& x._day == y._day;
}bool operator<(const Time& x, const Time& y)
//bool TimeCompare2(const Time& x, const Time& y)
{if (x._year < y._year){return true;}else if (x._year == y._year){if (x._month < y._month){return true;}else if (x._month == y._month){return x._day < y._day;}}return false;
}int main()
{Time t1(2024, 1, 28);Time t2(2024, 2, 27);cout << operator==(t1, t2) << endl;cout << operator<(t1, t2) << endl;return 0;
}
同样的对于cout部分也可以写成:
int main()
{Time t1(2024, 1, 28);Time t2(2024, 2, 27);cout << (t1 == t2) << endl; //等价于cout << (operator==(t1,t2)) << endl;cout << (t1 < t2) << endl;return 0;
}
可以写成(t1 == t2)的原因是编译器帮我们做了一些处理,但是需要注意的是,在上述函数中我们将成员变量时的访问权限变为了public,但是实际情况中成员变量的访问权限是private,为此我们可以将定义的函数直接写入类中来解决这一问题:
但是当我们将运算符重载函数原封不动的移入类中时,会发现系统提示函数的参数太多了,这是因为除了x和y两个参数外还有一个隐藏的this指针,为此我们可以继续做出以下改变:
#include <iostream>
using namespace std;class Time
{
public:Time(int year, int month,int day){_year = year;_month = month;_day = day;}bool operator==(const Time& y){return _year == y._year&& _month == y._month&& _day == y._day;}bool operator<( const Time& y)//bool TimeCompare2(const Time& x, const Time& y){if (_year < y._year){return true;}else if (_year == y._year){if (_month < y._month){return true;}else if (_month == y._month){return _day < y._day;}}return false;}private:int _year;int _month;int _day;
};int main()
{Time t1(2024, 1, 28);Time t2(2024, 2, 27);cout << t1.operator==(t2) << endl; //仍然可以写成cout << (t1 == t2) << endl;cout << t1.operator<(t2) << endl; //仍然可以写成cout << (t1 < t2) << endl;return 0;
}
我们删除了参数const Time&x,这是因为我们放弃了将两个对象的成员变量放在一个比较函数进行比较的方法,而是调用t1类中的运算符重载函数,并将t1的this指针和t2的地址传递过去
#include <iostream>
using namespace std;class Time
{
public:Time(int year, int month,int day){_year = year;_month = month;_day = day;}void Print(){cout << this << endl;}bool operator==(const Time& y){cout << this << endl;return _year == y._year //等价于this->_year == y._year&& _month == y._month&& _day == y._day;}bool operator<( const Time& y){cout << this << endl;if (_year < y._year){return true;}else if (_year == y._year){if (_month < y._month){return true;}else if (_month == y._month){return _day < y._day;}}return false;}private:int _year;int _month;int _day;
};int main()
{Time t1(2024, 1, 28);t1.Print();Time t2(2024, 2, 27);cout << t1.operator==(t2) << endl;cout << t1.operator<(t2) << endl;return 0;
}

2、不是所有运算符都需要(支持) 运算符重载
3、函数重载的意义是可以存在同名但是其它内容不同的重名函数,运算符重载的意义是可以让两个自定义类型的对象进行比较
4、operato后的操作符必须是C/C++语法中存在的
operator@ //wrong
5、重载操作符必须有一个自定义类型的参数(不能去重载运算符改变内置类型的行为)
6、用于内置类型的运算符,其含义不能改变(是大于号但是重载时按小于的逻辑实现)
7、运算符重载函数作为类的成员函数重载时,其形参看起来要比操作数数目少1(因为成员函数的第一个参数为隐藏的this指针)
8、“.*”、“::”、“sizeof”、“?:”、“.”这五个运算符不能重载
.*:调用成员函数的指针时会使用
typedef重命名函数指针时,新名字要写在括号内:typedef void(*xinmingzi) ()
赋值运算符重载
拷贝构造:同类型的一个存在的对象初始化要创建的对象
赋值重载:两个已经存在的同类型的对象,一个拷贝赋值给另一个
#include <iostream>
using namespace std;class Time
{
public:Time(int year, int month,int day){_year = year;_month = month;_day = day;}bool operator==(const Time& y){cout << this << endl;return _year == y._year&& _month == y._month&& _day == y._day;}bool operator<(const Time& y){cout << this << endl;if (_year < y._year){return true;}else if (_year == y._year){if (_month < y._month){return true;}else if (_month == y._month){return _day < y._day;}}return false;}void operator=(const Time& y){_year = y._year;_month = y._month;_day = y._day;}void Print(){cout << _year << "-" << _month << "=" << _day << endl;}private:int _year;int _month;int _day;
};int main()
{Time t1(2024, 1, 28);Time t2(2024, 2, 27);t1 = t2;t1.Print();t2.Print();return 0;
}

连续赋值
一般情况下的赋值运算符可以进行连续赋值:
//连续赋值
int i,j;
i = j = 10
过程描述:10赋值给j,表达式j = 10的返回值是j,然后返回值j作为表达式i = j的右操作数赋值给i

所以,赋值重载应该也应该可以进行连续赋值,但是会报错:
#include <iostream>
using namespace std;class Time
{
public:Time(int year, int month,int day){_year = year;_month = month;_day = day;}bool operator==(const Time& y){cout << this << endl;return _year == y._year&& _month == y._month&& _day == y._day;}bool operator<( const Time& y){cout << this << endl;if (_year < y._year){return true;}else if (_year == y._year){if (_month < y._month){return true;}else if (_month == y._month){return _day < y._day;}}return false;}void operator=(const Time& y){_year = y._year;_month = y._month;_day = y._day;}void Print(){cout << _year << "-" << _month << "=" << _day << endl;}private:int _year;int _month;int _day;
};int main()
{Time t1(2024, 1, 28);Time t2(2024, 2, 27);Time t3(t1);t1 = t2 = t3;t1.Print();t2.Print();return 0;
}

这是因为t2 = t3的表达式没有返回值,实际上这里应该有一个返回值t2(跟普通的连续赋值一样) ,然后返回值t2应该作为t1 = t2这个表达式的右操作数,最后将t2赋值给t1,所以赋值重载函数应该有一个返回值,返回的应该是Time类的对象(有可能是t2也有可能是t1):
Time& operator=(const Time& y)
{_year = y._year;_month = y._month;_day = y._day;return *this;
}
注意事项:
1、返回值不是this而是*this,因为this是指针而我们想要返回的是该指针指向的对象
2、返回值类型应该是Time&而不是Time,因为返回类型是Time的话就是传值返回,传值返回的是*this(某个对象)的拷贝,这表示还要去调用拷贝构造函数,会造成无限递归
3、为了防止不必要自我的赋值,在赋值开始时会进行检查
Time& operator=(const Time& y)
{if(this != &y) //这里的&是取地址,判断this指向的地址和对象y的地址是否相同{_year = y._year;_month = y._month;_day = y._day;}return *this;
}
4、 没有显示定义赋值重载函数,编译器生成一个默认赋值重载函数,进行浅拷贝(内置类型的成员变量直接赋值,自定义类型的成员变量需要调用对应类的赋值重载函数)如果类中未涉及资源管理(开辟空间)赋值重载函数是否实现都可以,一旦涉及到资源管理必须实现赋值重载函数

5、C++规定赋值重载不能重载全局(大于小于可以重载成全局的,因为它们不是默认赋值重载函数)
原因:赋值运算符如果不显示实现,编译器会生成一个默认的,此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符只能是类的成员函数
6、拷贝构造是同一类型存在的对象初始化要创建的对象、赋值是已经存在的两个对象,一个拷贝赋值给另一个
日期类的实现
Date.h文件
#pragma once
#include <iostream>
#include <assert.h>
using namespace std;//声明日期类Date
class Date
{public://全缺省构造函数,为程序提供默认的缺省值Date(int year = 1, int month = 1, int day = 1);//运算符重载函数,更好的反应出函数的所提供的服务//日期大小的逻辑判断bool operator>(const Date& d);//返回值为bool型,因为我们只需要知道到底是大于还是小于即可bool operator<(const Date& d);//采用传引用传参,传入的是右操作数的别名,且加了const修饰后该别名只有读权限,对别名的修改不会影响全局bool operator==(const Date& d);bool operator>=(const Date& d);bool operator<=(const Date& d);bool operator!=(const Date& d);//日期增加和减少的运算Date& operator+=(int d);//返回值类型是Date和Date&均可但是是Date类型时会发生数据拷贝,造成资源浪费Date operator+(int d);//+=采用Date&做返回值类型是为了效率,+采用Date做返回值类型是因为防止野引用Date operator-(int d);Date& operator-=(int d);//日期++和--的运算(需要做特殊处理,区分前置和后置++、--),特殊处理都是为了解决语法逻辑不自洽,Date&是因为前置++在函数中进行+1后直接返回即可(相当于走个过场)用引用做返回值类型可以减少拷贝,Date是因为后置++返回的是原对象的拷贝虽然原对象也进行了++操作但是不能让它作为返回值,如果也是Date&由于tmp在函数结束时就会销毁会造成野引用的情况Date& operator++();//++d1Date operator++(int);//d1++,为了跟前置++区分,需要强行增加一个int形参,从而构成operator++()和operator++(int)之间的函数重载Date& operator--();//--d1Date operator--(int);//d1--//日期减日期(运算符重载函数同时也可以构成函数重载,这里的int operator-和Date operator-构成函数重载)int operator-(const Date& d);//返回的是两个日期之间的差值,同时不能修改传入的日期void Print(){cout << _year << "-" << _month << "-" << _day << endl;}//根据月份获取本月天数(本质是inline)//即使没有显式使用 inline 关键字,类中定义的函数也可能被视为内联函数。//在C++中,将函数定义放在类声明内部(通常是在类定义头文件中)的成员函数会被隐式视为内联函数。//当你把一个成员函数的定义直接放在类声明里时,编译器会自动将其视为内联函数。// 这种情况下,编译器会尝试优化这些成员方法以减少调用开销,并且不需要额外的 inline 关键字来指示。int GetMonthDay(int year,int month) //省去了if...else的判断,直接将月份写在数组中,同时为了能够使输入的月份能与数组中该月份的天数对应,创建了一个13个元素大小的数组,数组首元素为0,当传入月份为1时monthDays[1]刚好是31天{assert(month > 0 && month < 13);//月份的大小要在合理的范围内static int monthDays[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };//该函数会被频繁调用,所以为了避免每次调用函数的时候都会创建数组,应该在静态区开辟数组(生命周期延长至全局)if (month == 2 &&((year % 4 == 0) && year % 100 != 0) || (year % 400 == 0) )//只有month==2时才需要进行模运算,逻辑短路{return 29;}return monthDays[month];//返回当前月对应的天数} //检查流提取时的无效日期:流提取时输入2024 2 30可是二月根本没有三十天,但是如果没有检查函数的话编译器不认为这是错的,所以需要在人为初始化对象的时候(全缺省参数)以及流提取时进行无效日期的判断bool CheckInvalid();//看起来没参数,但是类中的成员函数有一个隐藏的this指针参数//友元声明(朋友声明)friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);//友元声明允许一个函数或类访问另一个类的私有成员。//通过使用友元声明,可以授权指定的外部函数或类来访问另一个类的私有成员,而无需将这些外部函数或类作为该类的成员函数//友元声明通常在被授权访问私有成员的外部实体(如全局函数、其他类)中进行。下面是一些关于友元声明使用方式和语法示例://注意事项://友元关系不具备传递性。//类之间互为友好时,它们各自可见对方所有非私密信息。//应慎用友好机制以避免破坏封装性原则。private:int _year;int _month;int _day;
};//流插入运算重载,目的是为了让除了内置类型的对象能用流插入运算符,自定义类型的对象也可以用流插入
//流插入运算符属于iostraem类中的ostream对象
//将该运算符重载函数放在类外是因为<<是双目运算符,在类中该函数的形式operator<<(ostream& out),而类中的成员函数有一个默认的this指针作为第一个形参,所以实际上的形式是operator<<(this,ostream& out),而cout<<d1,很明显调用时运算符左右两侧的类型是相反的,那么就需要写成d1 << cout 的形式,但是这就表示控制台流入到日期类中(不会报错,但是倒反天罡)
//根本原因:operator<<作为成员函数重载,this指针必须占据第一个参数,Date必须是左操作数
//因此为了让ostream作为第一个参数必须让该函数是全局函数
//但是此时就无法直接访问private的成员变量了,
ostream& operator<<(ostream& out, const Date& d);//全局函数不能放在.h中否则容易出现重定义(两个.cpp文件都会包含该函数.h)
//ostream&是为了满足多次流插入的需求写的,如果只进行一次流插入可以写成void//道理基本相同,流提取运算符属于iostream类中的istraem对象
istream& operator>>(istream& in, Date& d);//这里不加const的原因是,流提取的值要放在日期类中这个日期类碧血得是可以被读写的,加了const就不能更改日期类
Date.cpp文件
#include "Date.h"//以下的所有函数都位于类域中,这就导致以往的声明与定义分离时在.cpp文件中直接写函数名即可,
// 但是位于特定类域中的函数只有在指明函数所在的类域时才能使用,故在函数名前加上了域作用限定符//全缺省的默认构造函数(确定默认值,函数声明和定义分离时缺省值未避免歧义只能出现一次,最好是在声明中给与)
Date::Date(int year, int month, int day)//全缺省的默认构造函数不需要返回值
{_year = year;_month = month;_day = day;if (!CheckInvalid())//人为初始化的时候(上来就定义d2(2024,2,30))也得检查{cout << "构造日期非法" << endl;}
}//小于运算
bool Date::operator<(const Date& y)
{if (_year < y._year){return true;}else if (_year == y._year){if (_month < y._month){return true;}else if (_month == y._month){return _day < y._day;}}return false;
}//小于等于
bool Date::operator<=(const Date& y)
{return *this < y || *this == y;//this是指向的是对象d1空间的指针,对this的解引用可以获得d1对象的值
}
//涉及对象判断、加减的运算符在运算时都会去调用我们自定义的运算符重载函数
//如果判断d1<=d2,在进入该运算符重载函数时,会先调用小于运算符重载函数的情况,虽然看起来只是一个<,实际上却是(*this).operator<(y),可以写成一个简单的<是因为编译器替我们做了处理
//判断*this是否小于y,如果小于就返回真否则为假,由于总的目的是判断d1是否小于等于d2,所以满足小于或等于一个条件即可返回真//大于
bool Date::operator>(const Date& y)
{return !(*this <= y);//*this<=y的结果如果为真,即d1<=d2为真,再加上一个逻辑取反运算符!,就可以在逻辑上表明d1是不大于d2的,合理的运用逻辑取反运算符可以省略很多代码
}//判断小于等于就会调用operator<=,进而调用operator<和operator==//大于等于
bool Date::operator>=(const Date& y)
{return !(*this < y);//原理同上
}//等于
bool Date::operator==(const Date& y)
{return _year == y._year&& _month == y._month&& _day == y._day;
}//不等于
bool Date::operator!=(const Date& y)
{return !(*this == y);//原理同上
}//日期加天数(如果将该函数作为operator+,那么该函数就会导致d2 = d1 + 20得到d2时,d1的值也会发生改变,毕竟是this->_day = day,所以为了避免对d1的修改应该传入一个d1的拷贝代替d1进行运算)
Date& Date::operator+=(int day)
{//此时该函数传入的this指针是tmp对象的地址_day += day;//_day = 29+20 = 49//cout<<"还没进来" << endl;//t天大于当前月应该的天数,天数就应该减去当前月对应的天数,同时将月数++,进行下一个月的判断while (_day > GetMonthDay(_year, _month))//此时调用GetMonthDay函数,49>(该函数的返回值是){//cout<<"进来了" << endl;_day -= GetMonthDay(_year, _month);++_month;if (_month == 13)//如果天数大到把12个月份的所有天数都减完,此时的_month的值应该就是13{++_year;//_month=13证明_day都比365或366都大,此时年份+1_month = 1;//重现将月份置为1,再次开始循环判断}}return *this;//返回类型为tmp对象的别名,而不是tmp对象的值当函数返回一个对象(而不是引用)时,会调用拷贝构造函数来创建一个新的对象,并将其作为函数的返回值。这意味着会发生一次数据复制
}//返回引用允许直接操作原始对象而不是在内存中创建副本。这样可以避免额外开销和数据复制//日期加天数
Date Date::operator+(int day)//这里的返回值类型是Date,函数结束后tmp被销毁,如果使用Date&作为返回值,返回给d2的是tmp的别名,但是tmp所在空间已经被销毁里面的值也不保证有效了,而Date的话就可以将tmp的值放入临时变量中最后交给d2
{Date tmp = *this;//拷贝构造,等价于Date tmp(*this)tmp += day;//tmp = tmp + day,此时应该调用operator+=,且此时传入的day也是原来我们提供的数字return tmp;//为了防止d1+20对d1本身也产生了影响所以这里要用一个Date类型对象来做d1对象的拷贝,对tmp的修改无法影响d1
}//日期减天数
Date Date::operator-(int day)//Date防止野引用
{Date tmp = *this;tmp -= day;return tmp;
}//日期减天数
Date& Date::operator-=(int day)//Date&增加效率
{_day -= day;//用this->_day减去传递的天数while (_day <= 0)//如果天数大于_day,_day的结果就是负数,那么就将月份向前移动一月,月份减一,如果天数小于_day不进入循环直接将减去天数后的日期返回即可{--_month;if (_month == 0)//如果月份减一变为了零证明今年天数不够了,需要将年份向前移动一年,年份减一,同时将月份变为12月份{--_year;_month = 12;}_day += GetMonthDay(_year, _month);//_day在原有的为负数的基础上加上当前年当前月份的天数,如果结果大于等于零那么证明日期减天数的结果的确是该年该月}return *this;
}//前置++,编译器在调用时会自动识别++d1->d.operator++()
Date& Date::operator++()
{*this += 1;//调用operator+=,1表示天数(++日期嘛肯定是从天数上开始加的)return *this;//前置++:先++后使用,直接返回自己加一的结果即可
}//后置++,编译器在调用时会自动识别++d1->d.operator++(0),括号里的可以是0、1等整型常量只是起到标识作用
Date Date::operator++(int)
{Date tmp = *this;//拷贝构造,tmp是d1的拷贝*this += 1;//调用operator+=,1表示天数(++日期嘛肯定是从天数上开始加的)return tmp;//后置++:先使用后++,要先返回使用前的结果,然后再++,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)//这里的d是对象d2,而不是具体的天数
{int flag = 1;//标志,假设d1>d2,同时也可以用来表示运算结果的正负Date max = *this;//拷贝构造,将d1拷贝给max对象Date min = d;//拷贝构造将d2,将d2拷贝给min对象,因为我们默认传入的左操作数比右操作数小,所以直接进行了拷贝//但是有可能我们将大小搞反了,所以还需要进行判断与修正if (*this < d)//如果d1<d2那么就将flag设置为-1,同时交换对象的值{int flag = -1;max = d;min = *this;}//反正到这一步max对象肯定是大日期对象的拷贝,min肯定是小日期对象的拷贝int n = 0;while (min != max)//operator!=的效率会比operator<的效率高,反正最后的循环终止条件是min==max,多次掉用更麻烦的operator<达到==还不如选择多次调用简单的operator!=达到=={++min;//直到min的日期加到和max的日期相等时才会跳出循环++n;//min的日期每++一次,n也跟着++}return n * flag;//如果flag最后为正,则d1 - d2时,d1>d2,结果肯定也为正,如果最后flag为负,则d1 - d2时,d1<d2,结果肯定为负
}ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;//自定义类型的本质还是内置类型,所以可以用operator<<重载读取自定义类型中的内置类型return out;//最后返回out的别名即可
}istream& operator>>(istream& in,Date& d)
{while (1){cout << "依次输入年月日:>";in >> d._year >> d._month >> d._day;if (!d.CheckInvalid())//检查输入日期是否有效{cout << "输入日期无效" << endl;}else{break;}}return in;
}bool Date::CheckInvalid()
{if (this->_year <= 0|| this->_month < 1 || this->_month >12|| this->_day < 1 || this->_day > GetMonthDay(this->_year, this->_month))//防止遗忘this指针这里就直接加上了{return false;}else{true;}
}
Test.cpp文件
#include "Date.h"int main()
{Date d1(2024, 2, 30);Date d2(2024, 8, 1);cout << d1 - d2 << endl;Date d3;operator<<(cout, d1);cout << d1;cout << d1 << d2;//<<运算符是左结合,所以先进行的是cout<<d1,然后cout<<d1结束后的返回值作为下一次流插入的左操作数“返回值 << d2”cin >> d3;cout << d3;return 0;
}
注意事项:拷贝构造、赋值和析构函数都不需要写
- 拷贝构造:不需要用同类对象初始化创建对象,日期对象的值我们已经提供了
- 赋值:不需要把一个对象赋值给另一个对象,日期对象的值我们已经提供了
- 析构:没有空间的开辟不需要写,编译器默认的就行
const成员
基本概念:const修饰的成员函数叫做const成员函数
格式:成员函数()const
注意事项:const修饰成员函数,本质上是在修饰成员函数隐藏的this指针,表明在该成员函数中不能对类的任何成员进行修改

Date d1(2024,1,31)被const修饰,所以调用Print函数时,&d1指向的内容应该是const Date*即可读不可写,但是在Print函数中this指针指向的却是可读可写的,这是权限的放大

结论:
- 成员函数如果是一个对成员变量只进行读访问的函数,建议加const,这样const对象和非const对象都可以使用
- 成员函数如果是一个对成员变量要进行读写访问的函数,不能加const,否则不能修改成员变量
- const对象不可以调用非const成员函数,权限放大
- 非const对象可以调用const成员函数,权限缩小
- const成员函数内不可以调用其它的非const成员函数,权限放大
- 非const成员函数内可以调用其它的conts成员函数,权限缩小
取地址及const取地址操作符重载
作用:返回普通对象和const对象的地址(取地址运算符&的重载)
注意事项:
1、这两个默认成员函数一般不用重定义,编译器默认生成
2、这两个运算符一般不需要重载,使用编译器生成的默认地址的地址重载即可
3、被用户自定义的默认成员函数不会由编译器提供
#include <iostream>
using namespace std;class Date
{
public ://允许普通对象拿到地址Date* operator&(){return this ;}//允许const对象拿到地址const Date* operator&()const{return this ;}private :int _year ; // 年int _month ; // 月int _day ; // 日
};int main()
{Date aa1;const Date aa2;cout << &aa1 <<endl;cout << &aa2 <<endl;return 0;
}
3、只有特殊情况下才需要重载(比如想让别人获取到指定的内容)
只允许const对象拿到地址,普通对象拿到空:
#include <iostream>
using namespace std;class Date
{
public ://普通对象拿到空地址Date* operator&(){return nullptr ;}//const对象拿到真地址(用编译器提供的)
private :int _year ; // 年int _month ; // 月int _day ; // 日
};int main()
{Date aa1;const Date aa2;cout << &aa1 <<endl;cout << &aa2 <<endl;return 0;
}
const对象拿到假地址,普通对象拿到真地址:
#include <iostream>
using namespace std;class Date
{
public ://普通对象拿到真地址Date* operator&(){return this ;}//const对象拿到假地址const Date* operator&()const{return (const Date*)0x00eeffee;//十六进制强转为const Date*地址}private :int _year ; // 年int _month ; // 月int _day ; // 日
};int main()
{Date aa1;const Date aa2;cout << &aa1 <<endl;cout << &aa2 <<endl;return 0;
}
~over~
相关文章:
C++的类与对象(五):赋值运算符重载与日期类的实现
目录 比较两个日期对象 运算符重载 赋值运算符重载 连续赋值 日期类的实现 Date.h文件 Date.cpp文件 Test.cpp文件 const成员 取地址及const取地址操作符重载 比较两个日期对象 问题描述:内置类型可直接用运算符比较,自定义类型的对象是多个…...
【uni-app小程序开发】实现一个背景色渐变的滑动条slider
先直接附上背景色渐变的滑动条slider uni-module插件地址:https://ext.dcloud.net.cn/plugin?id16841 最近做的一个用uni-appvue2开发的微信小程序项目中要实现一个滑动进度控制条,如下图所示: 1. 滑动条需要渐变背景色 2. 滑块的背景色需…...
Claude3横空出世:颠覆GPT-4,Anthropic与亚马逊云科技共启AI新时代
✨✨ 欢迎大家来访Srlua的博文(づ ̄3 ̄)づ╭❤~✨✨ 🌟🌟 欢迎各位亲爱的读者,感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢,在这里我会分享我的知识和经验。&am…...
【AI视野·今日NLP 自然语言处理论文速览 第八十三期】Wed, 6 Mar 2024
AI视野今日CS.NLP 自然语言处理论文速览 Wed, 6 Mar 2024 Totally 74 papers 👉上期速览✈更多精彩请移步主页 Daily Computation and Language Papers MAGID: An Automated Pipeline for Generating Synthetic Multi-modal Datasets Authors Hossein Aboutalebi, …...
【AI视野·今日Robot 机器人论文速览 第八十二期】Tue, 5 Mar 2024
AI视野今日CS.Robotics 机器人学论文速览 Tue, 5 Mar 2024 Totally 63 papers 👉上期速览✈更多精彩请移步主页 Interesting: 📚双臂机器人拧瓶盖, (from 伯克利) website: https://toruowo.github.io/bimanual-twist 📚水下抓取器, (from …...
流量分析-webshell管理工具
文章目录 CSCS的工作原理CS流量特征 菜刀phpJSPASP 蚁剑冰蝎哥斯拉 对于常见的webshell管理工具有中国菜刀,蚁剑,冰蝎,哥斯拉。同时还有渗透工具cobaltstrike(CS)。 CS CobaltStrike有控制端,被控端,服务端。(相当于黑…...
备考2025年AMC8数学竞赛:吃透2000-2024年600道AMC8真题就够
我们继续来随机看五道AMC8的真题和解析,根据实践经验,对于想了解或者加AMC8美国数学竞赛的孩子来说,吃透AMC8历年真题是备考最科学、最有效的方法之一。 即使不参加AMC8竞赛,吃透了历年真题600道和背后的知识体系,那么…...
基于鹦鹉优化算法(Parrot optimizer,PO)的无人机三维路径规划(提供MATLAB代码)
一、无人机路径规划模型介绍 无人机三维路径规划是指在三维空间中为无人机规划一条合理的飞行路径,使其能够安全、高效地完成任务。路径规划是无人机自主飞行的关键技术之一,它可以通过算法和模型来确定无人机的航迹,以避开障碍物、优化飞行…...
linux Shell 命令行-02-var 变量
拓展阅读 linux Shell 命令行-00-intro 入门介绍 linux Shell 命令行-02-var 变量 linux Shell 命令行-03-array 数组 linux Shell 命令行-04-operator 操作符 linux Shell 命令行-05-test 验证是否符合条件 linux Shell 命令行-06-flow control 流程控制 linux Shell 命…...
C#MQTT编程10--MQTT项目应用--工业数据上云
1、文章回顾 这个系列文章已经完成了9个内容,由浅入深地分析了MQTT协议的报文结构,并且通过一个有效的案例让伙伴们完全理解理论并应用到实际项目中,这节继续上马一个项目应用,作为本系列的结束,奉献给伙伴们&#x…...
exceljs解析和生成excel文件
安装 npm install exceljs解析excel 通过 Workbook 的 readFile 方法可以拿到workbook对象, workbook对象包含的概念有 worksheet(工作表) --> row(行) --> cell(单元格).于是可以通过依次遍历 worksheet, row, cell来拿到单元格的数据直接通过 worksheet.getSheetValue…...
HCIP —— BGP 路径属性 (上)
目录 BGP 路径属性 1.优选Preferred-Value属性值最大的路由 2.优选Local-preference 属性数值大的路由 3.本地始发的BGP路由优先于其他对等体处学习到的路由。 4..优选AS_PATH属性值最短的路由 BGP 路径属性 BGP的路由选路是存在优选规则的,下图为华为官网提供…...
NIO学习总结(二)——Selector、FileLock、Path、Files、聊天室实现
一、Selector 1.1 Selector简介 1.1.1 Selector 和 Channel的关系 Selector 一般称为选择器 ,也可以翻译为 多路复用器 。 它是 Java NIO 核心组件中的一个,用于检查一个或多个 NIO Channel(通道)的状态是否处于可读、可写。由…...
面试经典150题(111-113)
leetcode 150道题 计划花两个月时候刷完之未完成后转,今天(第5天)完成了3道(111-113)150 111.(172. 阶乘后的零)题目描述: 给定一个整数 n ,返回 n! 结果中尾随零的数量。 提示 n! n * (n - 1…...
iOS17.4获取UDID安装mobileconfig描述文件失败 提示“安全延迟进行中”问题 | 失窃设备保护
iOS17.4这两天已经正式发布, 在iOS 17.4版本中新增了一个名为"失窃设备保护"的功能,并提供了一个"需要安全延迟"的选项。 iOS17.4获取UDID安装mobileconfig描述文件失败 提示“安全延迟进行中”问题 | 失窃设备保护 当用户选择启用…...
List--splice使用技巧
splice : 拼接两个list api: void dump(list<int>& li) {for(auto & i :li)cout<<i<< " ";cout<<endl; } int main() { list<int> li1 {1,3,5};list<int> li2 {2,4,6}; }1 c.splice(pos,c2); // li的开头插入li2链表…...
【最新版】ChatGPT/GPT4科研应用与AI绘图论文写作(最新增加Claude3、Gemini、Sora、GPTs技术及AI领域中的集中大模型的最新技术)
2023年随着OpenAI开发者大会的召开,最重磅更新当属GPTs,多模态API,未来自定义专属的GPT。微软创始人比尔盖茨称ChatGPT的出现有着重大历史意义,不亚于互联网和个人电脑的问世。360创始人周鸿祎认为未来各行各业如果不能搭上这班车…...
离线数仓(六)【ODS 层开发】
前言 1、ODS 层开发 ODS层的设计要点如下: (1)ODS层的表结构设计依托于从业务系统同步过来的数据结构(JSON/CSV/TSV)。 (2)ODS层要保存全部历史数据,故其压缩格式应选择高压缩比的…...
PPT只要出现弹窗就闪退,Word和Excel都是正常的
1. 问题描述 PPT在常规使用下,能进行正常编辑和保存,但在使用过程中出现弹窗,类似于报错或者打开文件选项就会出现闪退,或者在添加新建页时选用右键添加时也会出现闪退。 找了很久的办法,才得到解决。记录一下。 2.…...
21、电源管理入门之芯片设计中的电源管理
目录 1. 关于PCSA和SCP 2. 关于PSCI和SCMI 3. 关于芯片SoC设计中的一些要点 参考: 这里以ARM为例来进行说明,我们在做驱动软件的时候,就需要跟硬件SoC里面的IP打交道,通过操作寄存器来实现硬件功能。之前的文章:ARM SCP入门-AP与SCP通信中3和4章节已经进行了简单介绍,…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...
【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...
上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式
简介 在我的 QT/C 开发工作中,合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式:工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...
【无标题】湖北理元理律师事务所:债务优化中的生活保障与法律平衡之道
文/法律实务观察组 在债务重组领域,专业机构的核心价值不仅在于减轻债务数字,更在于帮助债务人在履行义务的同时维持基本生活尊严。湖北理元理律师事务所的服务实践表明,合法债务优化需同步实现三重平衡: 法律刚性(债…...
数据库——redis
一、Redis 介绍 1. 概述 Redis(Remote Dictionary Server)是一个开源的、高性能的内存键值数据库系统,具有以下核心特点: 内存存储架构:数据主要存储在内存中,提供微秒级的读写响应 多数据结构支持&…...
【若依】框架项目部署笔记
参考【SpringBoot】【Vue】项目部署_no main manifest attribute, in springboot-0.0.1-sn-CSDN博客 多一个redis安装 准备工作: 压缩包下载:http://download.redis.io/releases 1. 上传压缩包,并进入压缩包所在目录,解压到目标…...

