类和对象(4)--《Hello C++ Wrold!》(6)--(C/C++)--赋值运算符重载,取地址和const取地址操作符重载
文章目录
前言
这期的话会讲解剩余的三个默认成员函数–赋值运算符重载,取地址和const取地址操作符重载
类的剩下三个默认成员函数
赋值运算符重载
运算符重载
作用:让eg:+在自定义类型中也能使用
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
eg: bool operator +(const Data& a)
延申: bool ret = 1<2;这样也是可以的:ret会 = true; operator左右操作数的取哪个跟左右结合性无关哈
1.不能通过连接其他符号来创建新的操作符:比如operator@
2.重载操作符必须有一个类类型参数或者枚举类型(成员函数的话,this也算类类型参数)
3.用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
.* :: sizeof ?: .(这个易忘)
注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
运算符重载是可以显示调用的
Date& operator=(const Date& d)//d1传给的是this指针,this == &d1{_year = d._year;_month = d._month;_day = d._day;}main函数里面 d1 = d2//等效为d1.operator=(d2),像这种两个操作数的都是这样看的,前面那个在operator前面……//operator=(&d1, d2)
要注意的是运算符重载和拷贝构造函数区分
d2 = d1;//属于已经存在的两个对象之间的复制拷贝的话--运算符重载函数//不是d1(d2)Date d2(d1)//用一个已经存在的对象初始化另一个对象--拷贝构造函数//等价于 Date d2 = d1;
赋值运算符重载
重载时一般要做到这几点才不会错:
参数类型:const 类型&
返回值类型:类型&–不能void,不然连续赋值就遭了
返回*this
注意把自己给自己赋值那个判断一下,提高效率
注意:赋值运算符一定要重载成成员函数,不然系统还会自己再生成一个
Date& operator=(const Date& d) { if(this != &d)//这个操作还行 { _year = d._year; _month = d._month; _day = d._day; } return *this; }
系统自动生成的赋值重载函数:(跟拷贝构造的这个规则差不多)
1.内置类型成员进行浅拷贝
2.自定义类型成员会去调用他的赋值重载
基本流运算符重载
printf处理不了自定义类型的东西,但是基本流运算符的使用者cin,cout可以
cin(类型是:istream)和cout(类型是:ostream)支持自动识别类型的原因:
1.内置类型是因为库里面实现了
2.支持自定义类型是因为自己写的函数重载
(这个的函数重载的例子也在下面的日期类实现那里)
注意:基本运算符不能写成成员函数
可以显示调用:
cout<<d1;--operator<<(cout,d1);
基本流运算符不能写成成员函数的原因:
要写成成员函数的话,对象要占用operator的左操作数才行(因为this指针会去悄咪咪的占了),写出来就会是下面这个样子,不符合使用习惯
void operator<<(ostream& out);
取地址及const取地址操作符重载
这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容!
关于const的引申
1.也是有权限缩小,平移,扩大的那个问题
const int a = 10;int*pb = &a;//这个就算权限扩大了,是不行的
2.成员函数后面加const之后,普通的对象和const过了的对象都可以调用
成员函数加const:void Print()const这样加const才表示让this指针指向的对象不能被修改
总结对第二点:
只要成员函数内部不修改成员变量,都应该加const,这样const对象和普通对象都可以调用
const对象:比如:const d1(2025,5,7);在调用时:d2.Print();相当于d2.Print(&d2);this指向d2,所以有了上面那个说法
作业部分
设已经有A,B,C,D4个类的定义,程序中A,B,C,D析构函数调用顺序为?(B)C c;int main(){A a;B b;static D d; return 0;}A.D B A CB.B A D CC.C D B AD.A B D C构造顺序是按照语句的顺序进行构造,析构是按照构造的相反顺序进行析构然后因为有static,所以d的生存周期变了,但是没C长
下列关于赋值运算符“=”重载的叙述中,正确的是(A)A.赋值运算符只能作为类的成员函数重载//理解B.默认的赋值运算符实现了“深层复制”功能C.重载的赋值运算符函数有两个本类对象作为形参//这里的形参指的是()里面的D.如果己经定义了复制拷贝构造函数,就不能重载赋值运算符
&作取地址和引用的区分:在描述类型时的&才是引用
日期类的部分实现class Date{friend ostream& operator<<(ostream& out, const Date& d);public:// 获取某年某月的天数int GetMonthDay(int year, int month){static int daysArr[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)if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))//这样更好{return 29;}else{return daysArr[month];}} // 全缺省的构造函数Date(int year = 1900, int month = 1, int day = 1); // 拷贝构造函数// d2(d1)Date(const Date& d); // 赋值运算符重载Date& operator=(const Date& d); // 析构函数~Date();Date& Date::operator+=(int day){ if(day GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);++_month;if (_month == 13)//这里不是用的while{++_year;_month = 1;}}return *this;}Date Date::operator+(int day){Date tmp(*this);tmp += day;//就像这样,用到之前已经实现了的部分来简化return tmp; // 前置++Date& operator++(){*this += 1;return *this;} // 后置++Date Date::operator++(int){Date tmp = *this;*this += 1;return tmp;} // 日期-日期 返回天数int operator-(const Date& d) const{Date max = *this;Date min = d;int flag = 1;if (*this < d){max = d;min = *this;flag = -1;//表明那个d才是大的}int n = 0;while (min != max){++min;++n;}return n * flag;//flag的巧用,比if来搞好}int GetYear(){ return _year;//这种方法能让私有的能拿出去}private:int _year;int _month;int _day;};ostream& operator<<(ostream& out, const Date& d)//不能改成 const ostream& out{out << d._year << \"年\" << d._month << \"月\" << d._day << \"日\" <>(istream& in, Date& d)//注意这两个的类型!{int year, month, day;in >> year >> month >> day;//感觉还是上面那种读法好些if (month > 0 && month 0 && day <= d.GetMonthDay(year, month)){d._year = year;d._month = month;d._day = day;}else{cout << \"非法日期\" << endl;assert(false);//这后面的操作是防止用户输入错误日期}
注意:前置不用加int,后置要加int这个是规定,不能颠倒,参数也不能换成其他类型,让这两个构成重载
在上面的基础上,用eg:d1++;那么编译器就会调用d1.operator(0)//调用时这的int是0
注意:前置返回++之前的对象,后置返回++之后的对象(用来实现前后置效果)
注意:对于内置类型来说,前置和后置的代价差不多,可以忽略不计;但是对自定义类型来说,前置比后置代价小不少(后置需要多构造一个临时对象),平时应该多用前置的