运算符重载(关键字operator的使用)
1:运算符重载的方法
运算符重载的方法有三种,一种是将运算符重载函数写在类中,作为类的成员函数;另一种是将运算符重载函数写在类外,当该函数需要访问类的私有或者受保护成员时,在类中将该函数声明为友元函数;第三种是将运算符重载函数写在类外,且该函数不需要访问类的私有或受保护成员,则该函数是普通函数。
主要讲和类相关的运算符重载。
重载为成员函数
在类中重载运算符的格式是
函数类型 operator $(形式参数表){..........}
$代表被重载的运算符
看下面日期类的运算符重载
class Date
{
public:
// 获取某年某月的天数
int GetMonthDay(int year, int month);
// 全缺省的构造函数
Date(int year = 1900, int month = 1, int day = 1) :_year(year), _month(month), _day(day) {};
// 拷贝构造函数
// d2(d1)
Date(const Date& d);
// 赋值运算符重载
// d2 = d3 -> d2.operator=(&d2, d3)
Date& operator=(const Date& d);
// 析构函数
~Date();
// 日期+=天数
Date& operator+=(int day);
// 日期+天数
Date operator+(int day);
// 日期-天数
Date operator-(int day);
// 日期-=天数
Date& operator-=(int day);
// 前置++
Date& operator++();
// 后置++
Date operator++(int);
// 后置--
Date operator--(int);
// 前置--
Date& operator--();
// >运算符重载
bool operator>(const Date& d);
// ==运算符重载
bool operator==(const Date& d);
// >=运算符重载
bool operator >= (const Date& d);
// <运算符重载
bool operator < (const Date& d);
// <=运算符重载
bool operator <= (const Date& d);
// !=运算符重载
bool operator != (const Date& d);
// 日期-日期 返回天数
int operator-(const Date& d)const;
private:
int _year;
int _month;
int _day;
};
上面函数我们只是在类中声明了运算符的重载还没有具体实现,我们在另外一个文件中对运算符的重载进行定义
int Date::GetMonthDay(int year, int month)
{
assert(_month <= 12 &&_month>=1);
static int arr[13] = { -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;
}
return arr[month];
}
Date::Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
Date& Date::operator=(const Date& d)
{
this->_year = d._year;
this->_month = d._month;
this->_day = d._day;
return *this;
}
Date::~Date()
{
_year = _month = _day = 0;
}
Date& Date::operator+=(int day)
{
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month == 13)
{
_year++;
_month = 1;
}
}
return *this;
}
Date Date::operator+(int day)
{
Date tmp(*this);
tmp += day;
return tmp;
}
Date& Date::operator-=(int day)
{
_day -= day;
while (_day <= 0)
{
_day += GetMonthDay(_year, _month);
_month--;
if (_month == 0)
{
_month = 12;
_year--;
}
}
return *this;
}
Date Date::operator-(int day)
{
Date tmp(*this);
tmp -= day;
return tmp;
}
Date& Date::operator++()
{
*this += 1;
return *this;
}
Date Date::operator++(int)
{
Date tmp(*this);
*this += 1;
return tmp;
}
Date Date::operator--(int)
{
Date tmp(*this);
*this += 1;
return tmp;
}
Date& Date::operator--()
{
*this -= 1;
return *this;
}
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)
{
return _day > d._day;
}
}
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 || *this == d;
}
bool Date::operator != (const Date& d)
{
return !(*this == d);
}
int Date::operator-(const Date& d)const
{
Date max(*this);
Date min(d);
int flag = 1;
if (max < min)
{
Date tmp = max;
max = min;
min = tmp;
flag = -1;
}
int n = 0;
while (min != max)
{
min++;
n++;
}
return n * flag;
}
2:运算符重载规则
1:当运算符被⽤于类类型的对象时,C++语⾔允许我们通过运算符重载的形式指定新的含义。C++规定类类型对象使⽤运算符时,必须转换成调⽤对应运算符重载,若没有对应的运算符重载,则会编译报错。
2:运算符重载是具有特殊名字的函数,他的名字是由operator和后⾯要定义的运算符共同构成。和其他函数⼀样,它也具有其返回类型和参数列表以及函数体。
3:重载运算符函数的参数个数和该运算符作⽤的运算对象数量⼀样多。⼀元运算符有⼀个参数,⼆元运算符有两个参数,⼆元运算符的左侧运算对象传给第⼀个参数,右侧运算对象传给第⼆个参数。
4:如果⼀个重载运算符函数是成员函数,则它的第⼀个运算对象默认传给隐式的this指针,因此运算符重载作为成员函数时,参数⽐运算对象少⼀个。
5:运算符重载以后,其优先级和结合性与对应的内置类型运算符保持⼀致。
6:不能通过连接语法中没有的符号来创建新的操作符:⽐如operator@。
7:.* :: sizeof ? : .注意以上5个运算符不能重载。(选择题⾥⾯常考,⼤家要记⼀) 操作符⾄少有⼀个类类型参数,不能通过运算符重载改变内置类型对象的含义,如: intoperator+(int x, int y)
8:⼀个类需要重载哪些运算符,是看哪些运算符重载后有意义,⽐如Date类重载operator - 就有意义,但是重载operator + 就没有意义。
9:重载++运算符时,有前置++和后置++,运算符重载函数名都是operator++,⽆法很好的区分。
10:C++规定,后置++重载时,增加⼀个int形参,跟前置++构成函数重载,⽅便区分。
11:重载 << 和 >> 时,需要重载为全局函数,因为重载为成员函数,this指针默认抢占了第⼀个形参位置,第⼀个形参位置是左侧运算对象,调⽤时就变成了 对象 << cout,不符合使⽤习惯和可读性。重载为全局函数把ostream / istream放到第⼀个形参位置就可以了,第⼆个形参位置当类类型对象。
3:日期类中没有的运算符重载
我们在日期类中重载了单目运算符,关系运算符,逻辑运算符,赋值运算符的重载。我们现在来说明一下其他运算符的重载
1:位运算符的重载
class Array {
public:
Array(int d[], int nn)
{
_n = nn;
_data = new int[_n];
for (int i = 0; i < _n; i++)
{
_data[i] = d[i];
}
}
~Array()
{
if (_data != nullptr)
{
delete[] _data;
}
}
void show()
{
for (int i = 0; i < _n; i++)
{
cout << _data[i] << " ";
}
cout << endl;
}
Array& operator<<(int num)
{
int tmp;
for (int pass = 1; pass <= num; pass++)
{
tmp = _data[0];
for (int i = 0; i < _n - 1; i++)
{
_data[i] = _data[i + 1];
}
_data[_n - 1] = tmp;
}
return *this;
}
private:
int* _data, _n;
};
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9 };
Array a(arr, sizeof(arr) / sizeof(arr[0]));
a.show();
a << 578;
a.show();
return 0;
}
我们将位运算符重载成了,可以直接对数组进行左移多少位。
2:下标访问运算符的重载
int& operator[](int index)
{
if (index >= 0 && index < _n - 1)
{
return _data[index];
}
else
{
cout << "下标超出范围" << endl;
exit(1);
}
}
我们依然使用上面的数组代码,定义和声明都在类中不做分离。
3:输入输出流的重载
我给出两种方法
1:使用友元
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Array {
friend std::ostream& operator<< (std::ostream& out, const Array& data);
public:
Array(int d[], int nn)
{
_n = nn;
_data = new int[_n];
for (int i = 0; i < _n; i++)
{
_data[i] = d[i];
}
}
~Array()
{
if (_data != nullptr)
{
delete[] _data;
}
}
int& operator[](int index)
{
if (index >= 0 && index < _n)
{
return _data[index];
}
else
{
cout << "下标超出范围" << endl;
exit(1);
}
}
private:
int* _data;
int _n;
};
std::ostream& operator<<(std::ostream& out, const Array& data)
{
for (int i = 0; i < data._n; i++) {
out << data._data[i] << " ";
}
return out;
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9 };
Array a(arr, sizeof(arr) / sizeof(arr[0]));
cout << a << endl;
a[1] = 99;
cout << a << endl;
return 0;
}
2:定义两个接口
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Array {
public:
Array(int d[], int nn)
{
_n = nn;
_data = new int[_n];
for (int i = 0; i < _n; i++)
{
_data[i] = d[i];
}
}
~Array()
{
if (_data != nullptr)
{
delete[] _data;
}
}
int& operator[](int index)
{
if (index >= 0 && index < _n)
{
return _data[index];
}
else
{
cout << "下标超出范围" << endl;
exit(1);
}
}
const int* data() const
{
return _data;
}
int size() const
{
return _n;
}
private:
int* _data;
int _n;
};
std::ostream& operator<<(std::ostream& out, const Array& data)
{
for (int i = 0; i < data.size(); i++)
{
out << data.data()[i] << " ";
}
return out;
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9 };
Array a(arr, sizeof(arr) / sizeof(arr[0]));
cout << a << endl;
a[1] = 99;
cout << a << endl;
return 0;
}