🌈个人主页:羽晨同学
💫个人格言:“成为自己未来的主人~”
赋值运算符重载
运算符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型和参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型operator操作符(参数列表)
注意:
- 不能通过连接其他符号来创建新的操作符,比如operator@,这个其实完全没意义。
- 重载操作符必须有一个类类型参数,这个就证明了重载操作符是针对自定义类型的,不可能针对内置类型。
- 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义。
- 作为类成员函数重载时,其形参看起来比操作数少一,因为有一个隐藏的this指针。
- .* :: sizeof ?: .这五个运算符是不能重载的。
接下来我们先来讲讲这五个不能重载的运算符中的第一个。这个运算符是极其少见的。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class OB
{
public:
void func()
{
cout << "void func()" << endl;
}
};
typedef void(OB::* PtrFunc)();
int main()
{
PtrFunc fp = &OB::func;//成员函数取地址要加一个&,普通函数函数名就是地址
OB temp;
(temp.*fp)();
return 0;
}
其实.*的作用是什么呢,我们可以通过这段代码看到,.*的作用就是帮助我们调用成员函数的指针。
接下来,我们就回到了operator关键字的作用,这个关键字可以极大的提高代码的可读性,我们来看下面的代码。
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
bool operator==(const Date& d1, const Date& d2)
{
return d1._year == d2._year
&& d1._month == d2._month
&& d1._day == d2._day;
}
void Test()
{
Date d1(2018,9.26);
Date d2(2018, 9, 27);
cout << (d1 == d2) << endl;
}
这段代码是operator的全局调用,我们会看到如果全局调用的话会存在一个问题,那就是成员变量是私有的。我们无法保证封装性。
这里的解决办法有三种,
- 提供这些成员的get和set
- 友元
- 重载为成员函数
我们一般使用的就是第三种。重载为成员函数
什么意思呢?我们来看下面的这段代码
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
bool Func(const Date& d)
{
return this->_year == d._year
&& this->_month == d._month
&& this->_day == d._day;
}
bool operator==(const Date& d)
{
return this->_year == d._year
&& this->_month == d._month
&& this->_day == d._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d3(2024, 4, 14);
Date d4(2024, 4, 15);
cout << d3.operator==(d4) << endl;
cout << (d3 == d4) << endl;
return 0;
}
懂了吗,我们将重载函数放到成员函数里面,就避免了权限不够的问题。
赋值运算符重载
赋值运算符重载格式
- 参数类型:const T&,传递引用可以提高传参效率。
- 返回值类型: T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续复制
- 检测是否自己给自己复制
- 返回*this,要复合连续赋值的含义。
#include<iostream>
using namespace std;
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)
{
cout << " Date(const Date& d)" << endl;
_year = d._year;
_month = d._month;
_day = d._day;
}
Date& operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
~Date()
{
cout << "~Date()" << endl;
_year = -1;
_month = -1;
_day = -1;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2024, 4, 14);
Date d2(d1);
Date d3 = d1;
return 0;
}
2.赋值运算符只能重载成类的成员函数不能重载成全局函数
这个怎么理解呢?其实重载成类的成员函数和重载成全局函数有一个很大的不同点,就是成员函数还有一个潜在的this指针,而全局函数是没有这个东西,所以,如果要重载为全局函数的话,我们就需要给两个参数。
class Date
{
public:
Date(int year = 1949, int month = 10, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
Date& operator=(Date& left, const Date& right)
{
if (&left != &right)
{
left._year = right._year;
left._month = right._month;
left._day = right._day;
}
return left;
}
我们发现这个代码是会报错的, 原因是由于赋值运算符如果不显示实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中编译器自己生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。
用户没有显示实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝,注意:内置类型成员变量是直接赋值的,而自定义类型变量是需要调用对应类的赋值运算符重载完成赋值。
class Date
{
public:
Date(int year = 2024, int month = 3, int day = 4)
{
_year = year;
_month = month;
_day = day;
}
private:
int _month;
int _day;
int _year;
};
int main()
{
Date d1(2024, 5, 15);
Date d2;
d2 = d1;
return 0;
}
#include<iostream>
using namespace std;
class Time
{
public:
private:
int _hour = 1;
int _second = 1;
int _minute = 1;
};
class Date
{
public:
void Print()
{
cout << "class Time" << endl;
}
Date(int year = 2024, int month = 3, int day = 4)
{
_year = year;
_month = month;
_day = day;
}
private:
int _month;
int _day;
int _year;
Time _t;
};
int main()
{
Date d1(2024, 5, 15);
Date d2;
d2 = d1;
d1.Print();
d2.Print();
return 0;
}
通过这个结果,我们可以很明显的得出上面的结论。