开头:
在类与对象(二)————构造与析构函数-CSDN博客中我们介绍了默认成员函数六个里的前三个,了解过它们的功能并能写出符合我们要求的各式函数包括构造、析构、拷贝构造。本篇继续认识默认成员函数。
在从C语言到C++-CSDN博客中我们介绍过函数重载(遗忘的建议看一下),允许在C++中使用同名函数来提高代码灵活度,与今天我们要介绍的运算符重载无关,因为运算符重载的目的是改变运算符对自定义类型的行为。
已知运算符对内置类型的操作(如加减乘除比较大小),那怎么才能让自定义类型也能实现这些操作呢?实现的过程就叫运算符重载。
运算符重载:
特点:
1. 参数个数:与运算符的操作对象一样多,如果作为成员函数,第一个参数默认是*this(隐式),参数就少一个。
2.至少有一个自定义类型使用了该运算符,才会调用该运算符重载,而且自动调用。
3. " . " " .* " " :: " " sizeof " " ? : " 五个运算符不能重载。
4.要求重载后要求运算符与原运算符的优先级、结合性相同。
5.每一次出现自定义类型和该运算符都会调用重载函数。
eg:(仍以日期类实现)
// 获取某年某月的天数
inline int GetMonthDay(int year, int month)
{
static int monthday[13] = { -1,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;
else
return monthday[month];
}
该函数用于获取某年某月的天数,接下来会频繁使用,所以写成inline内联函数。
重载+=运算符:
// 日期+=天数 改变原来的日期,得到的是新日期
Date& operator+=(int day)
{
if (day < 0) //考虑day的正负
{
return (*this -= (-day));
}
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
++_month;
if (_month==13)
{
++_year;
_month = 1;
}
}
return *this;
}
运行时:
这样一来+=可以对Date类的对象使用,我们可以再重载+运算符。
与+=不同的是+运算符不能改变对象本身。因此我们要运用到this指针来保证对象本身不被改变
(详见从C语言到C++-CSDN博客)
// 日期+天数 不改变原来日期,就像i+10,不改变i一样
Date operator+(int day)
{
Date tmp = *this;
tmp += day;
return tmp;
}
这样一来我们就可以重载+、+=、-、-=、给类的对象使用。
而判断两个对象的大小关系用到的> < >= <= == !=
前置++ 后置++的重载:
前置++ 后置++(减减同理)相比以上的运算符重载更重要。
如果是前置++的话,先加1再返回使用该对象,相当于+=1:
Date& operator++()
{
*this += 1;
return *this;
}
后置++相比于前置++,是应该先使用该对象,再+=1:
Date operator++(int)
{
Date tmp = *this;
*this += 1;
return tmp;
}
注意:
1.两者之间前置++返回的是Date型引用减少了拷贝复制,而后置++要返回的是临时对象tmp,临时对象函数结束被销毁,所以不能传引用返回。
2.因为返回值类型不同不能作为函数重载的条件,所以为区别两函数,后置++函数参数多了一个int型参数,函数调用时正常使用即可,不需要传int参数
建议自己写一遍 前置-- 和 后置--,为方便读者将它们放在下面:
// 后置--
Date operator--(int)
{
Date tmp = *this;
*this -= 1;
return tmp;
}
// 前置--
Date& operator--()
{
*this -= 1;
return *this;
}
流插入 流提取 运算符重载:
在 C++ 中,<<
和 >>
运算符也被称为流插入运算符和流提取运算符。它们是重载的运算符,这意味着它们的行为取决于它们的操作数的类型。对于流对象(如 std::cout
和 std::cin
),这些运算符被重载为执行输入输出操作。(cout cin是 C++ 标准库中的两个流对象,cin
是 istream
类型的对象,cout
是 ostream
类型的对象,它们做参数时,只能引用传参。)
因为cout / cin也是<< / >>的操作对象, << / >>重载时肯定不能写成成员函数,因为成员函数第一个参数默认是this指针,要将类的对象作为第二操作数。
还要能连续输出,就需要return cout
//重载<<
ostream& operator<<(ostream& out,const Date& d)
{
out << d._year<<"/" << d._month << "/" << d._day << endl;
return out;
}
//重载>>
istream& operator>>(istream& in, Date& d)
{
cout << "请输入年月日:>" << endl;
in >> d._year >> d._month >> d._day;
return in;
}
赋值运算符重载:
定义:
1.用两个已经存在的对象直接拷贝赋值,一定要与拷贝构造函数区别开
2.特殊的运算符重载,规定必须是成员函数
也是要实现d1=d2=d3;(d1 d2 d3都是某类的对象),和拷贝构造函数不同的是d1 d2已经存在,而不是用d3初始化。
实现:
/ 赋值运算符重载
Date& operator=(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
很明显,赋值运算符重载 和 编译器自带的默认成员函数 也是浅拷贝,如果是Date类这种可以不写赋值运算符重载函数,一旦遇到stack类这种向内存申请空间的类,一定需要自己写,且需要深拷贝,具体与拷贝构造函数相似。
取地址运算符重载:
这是我们讲的六个默认成员函数里的最后一个,也是最省力的一个成员函数。
在这之前,我们要了解一下 const
const:
在 C++ 中,`const` 关键字用于声明常量,它如下几种用途:
1. 修饰变量:当 const 修饰一个变量时,这个变量的值在初始化后就不能被改变。
2. 修饰函数参数:当 const 修饰函数参数时,表示该参数在函数内部不能被修改。这通常用于传递对象时,防止对象在函数内部被修改。
3. 修饰成员函数:当 const 修饰成员函数时,表示这个成员函数不会修改对象的状态(即不会修改对象的任何成员变量)。这样的成员函数可以在常量对象上调用。
使用 const 可以提高代码的可读性和安全性,因为它明确了变量、参数或成员函数的行为。同时,它也有助于编译器进行优化。
回到 重载&函数:
有两个取地址运算符重载函数:
左边就是编译器里的默认成员函数,自动生成。
即便对const 类的对象取地址,也不会用到第二个,因为会自动生成。
所以还是不用自己写。
总结:
认识运算符重载函数,以及特殊的运算符重载。
百看不如一写,真正的掌握还需自己实践获得。