类和对象(4)--《Hello C++ Wrold!》(6)--(C/C++)--赋值运算符重载,取地址和const取地址操作符重载
文章目录
- 前言
- 类的剩下三个默认成员函数
- 赋值运算符重载
- 运算符重载
- 赋值运算符重载
- 基本流运算符重载
- 取地址及const取地址操作符重载
- 关于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 C
B.B A D C
C.C D B A
D.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<0) return *this-=-day;//防止day是负数;这个-=要自己实现!_day += day;while (_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 << "日" << endl;
//这里不能这样搞,除非设置成友元(像上面写的那样)
//对象的成员函数不是只能用成员变量用return out;
}istream& operator>>(istream& in, Date& d)//注意这两个的类型!
{int year, month, day;in >> year >> month >> day;//感觉还是上面那种读法好些if (month > 0 && month < 13&& day > 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
注意:前置返回++之前的对象,后置返回++之后的对象(用来实现前后置效果)
注意:对于内置类型来说,前置和后置的代价差不多,可以忽略不计;但是对自定义类型来说,前置比后置代价小不少(后置需要多构造一个临时对象),平时应该多用前置的