1.运算符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类 型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。 函数名字为:关键字operator后面接需要重载的运算符符号。 函数原型:返回值类型 operator操作符(参数列表)
注意:
不能通过连接其他符号来创建新的操作符:比如operator@ 重载操作符必须有一个类类型参数 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this。
.* :: sizeof ?: . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
2.赋值运算符重载
1.参数类型:const T&,传递引用可以提高传参效率
2.返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
3.检测是否自己给自己赋值
4.返回*this :要复合连续赋值的含义
5.赋值运算符只能重载成类的成员函数不能重载成全局函数
原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的 赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的 成员函数。
6.用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类 型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。(如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现。也就是深拷贝的样式)
#include<iostream>
using namespace std;
class MyClass {
private:
int data;
public:
MyClass(int d = 0) { data = d; }
// 重载赋值运算符
MyClass& operator=(const MyClass& other) {
// 检查自身赋值
if (this == &other) {
return *this;
}
// 执行赋值操作
data = other.data;
return *this;
}
};
int main() {
MyClass obj1(10);
MyClass obj2(20);
// 使用赋值运算符重载将obj2的值赋给obj1
obj1 = obj2;
return 0;
}
在上面的示例中,我们定义了一个名为MyClass
的类,并重载了赋值运算符"="。在main
函数中,我们创建了两个MyClass
对象obj1
和obj2
,并使用重载的赋值运算符将obj2
的值赋给obj1
。
3.完整Date类的运算符重载
3.1.cpp文件
#include"Date.h"
bool Date::ChekDate()
{
if (_month < 1 || _month>12 || _day<1 || _day>Get_month_day(_year, _month)) {
return false;
}
else {
return true;
}
}
Date::Date(int year,int month,int day) {
_year = year;
_month = month;
_day = day;
if (!ChekDate()) {
cout << "日期非法!" << endl;
return;
}
}
void Date:: print ()const
{
cout << _year << "_" << _month << "_" << _day << endl;
}
bool Date::operator<(const Date& d) {
if (_year < d._year)
{
return true;
}
else if (_year == d._year) {
if (_month < d._month) {
return true;
}
else if (_month == d._month) {
if (_day < d._day) {
return true;
}
}
}
else {
return false;
}
}
bool Date::operator==(const Date& d) {
return _year == d._year && _month == d._month && _day == d._day;
}
bool Date::operator<=(const Date& d) {
return *this < d || *this == d;
}
bool Date::operator>(const Date& d) {
return !(*this <= d);
}
bool Date::operator>=(const Date& d) {
return !(*this < d);
}
bool Date::operator!=(const Date& d) {
return !(*this == d);
}
//d1+=50
Date& Date:: operator+=(int day) {
_day += day;
while (_day > Date::Get_month_day(_year, _month)) {
_day -= Get_month_day(_year, _month);
++_month;
if (_month == 13) {
++_year;
_month = 1;
}
}
return *this;
}
Date Date::operator+(int day) {
Date tmp = *this;//拷贝构造
tmp += day;
return tmp;
//由于tmp为局部对象,返回后将销毁,若还是用&返回的话
//那么将会得到随机值,所以这里必须要拷贝构造成临时对象进行返回。
}
Date& Date:: operator-=(int day) {
_day -= day;
while (_day <= 0) {
_day += Get_month_day(_year, _month-1);
--_month;
if (_month == 0) {
--_year;
_month = 12;
}
}
return *this;
}
Date Date::operator-(int day) {
Date tmp = *this;//拷贝构造
tmp -= day;
return tmp;
}
//前置++
Date& Date::operator++() {
*this += 1;
return *this;
}
//后置++,祖师爷为了区分这俩的区别,给后置++给了一个int类型的形参进行区分
Date Date::operator++(int) {
Date tmp = *this;
*this += 1;
return tmp;//对于局部变量,必须使用拷贝构造出临时对象进行返回。
}
int Date:: operator-(const Date& d) {
Date max = *this;
Date min = d;
int flag = 1;
if (max < min) {
max = d;
min = *this;
flag = -1;
}
int n = 0;
while (max != min) {
++min;
++n;
}
return n * flag;//用flag来化解大小问题
}
3.2.h文件
#pragma once
#include<iostream>
#include<assert.h>
#include<stdbool.h>
using namespace std;
class Date {
public:
bool ChekDate();//用于检查构造出的日期是否非法。
Date(int year = 1990, int month = 1, int day = 1);
void print()const;
bool operator<(const Date& d);//比较的参数原本是两个,但是这里有一个this指针,
bool operator<=(const Date& d);//所以只需要传一个参数。
bool operator>(const Date& d);//参数至少要有一个是类类型。
bool operator>=(const Date& d);//用引用作为形参可以减少拷贝构造的消耗
bool operator==(const Date& d);//const持平权限.临时变量具有常性
bool operator!=(const Date& d);
//直接定义在类中的函数,就是内联函数(inline),不会进符号表。
//这个函数会被频繁调用
int Get_month_day(int year,int month) {
assert(_month > 0 && _month < 13);
//由于每次调用都会创建该数组,所以我们可以将其改为静态,这样就可以减少创建的消耗。
//静态数组在整个程序结束后才会销毁,这样每次调用该函数就不需要创建数组所需要的消耗。
static int Get_month_day_arry[] = { -1,31,28,31,30,31,30,31,31,30,31,30,31 };
if (month == 2 && (year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
return 29;
}
else {
return Get_month_day_arry[month];
}
}
//日期与天数
Date& operator+=(int day);
Date operator+(int day);
Date& operator-=(int day);
Date operator-(int day);
//前置++和后置++重载
Date& operator++();
Date operator++(int);
//日期减日期
int operator-(const Date& d);
private:
int _year;
int _month;
int _day;
};
前置++和后置++重载我们的前辈为了区别这俩,就给后置++增添了一个int类型参数,在上述代码中也有注释,供大家参考。
4.const成员
将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this 指针,表明在该成员函数中不能对类的任何成员进行修改。
当我们创建一个const修饰对象时,然后调用成员函数时,将对像的地址传给成员函数,也就是this指针来接收,this的完全形式是类型+const *this,我们知道这样只是限制了this,并没有限制*this,所以我们将成员函数的this也加上const,就有了上述的操作,这里其实是权限放大的问题,用const达到权限平衡。
5.取地址及const取地址操作符重载
这两个默认成员函数一般不用重新定义 ,编译器默认会生成。
class Date
{
public :
Date* operator&()
{
return this ;
}
const Date* operator&()const
{
return this ;
}
private :
int _year ; // 年
int _month ; // 月
int _day ; // 日
};
这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如不想让别人获取到指定的内容!
