🌈个人主页:羽晨同学
💫个人格言:“成为自己未来的主人~”
赋值运算符重载
运算符重载
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;
}
通过这个结果,我们可以很明显的得出上面的结论。