类和对象
五、赋值运算符重载
1、运算符重载
运算符重载是具有特殊函数名的函数,是C++为了增强代码可读性而引入的
operator sign(parameter);
operator为关键字,sign就是需要重载的运算符符号,parameter为参数(可以为多个)
注意事项:
不能通过连接其他符号来创建新的操作符
重载操作符至少有一个类类型参数
用于内置类型之间的运算符含义不改变,编译器会自动检测使用运算符的类型是什么,从而选择是否改变运算符含义
类成员函数重载时有一个隐藏的参数this
不能重载的五个运算符 |
---|
.* |
:: |
sizeof |
?: |
. |
重载一个==
//……全局
bool operator==(const Date& d1, const Date& d2)
{
return d1._year == d2._year
&& d1._month == d2._month
&& d1._day == d2._day;
}
//……
但定义一个全局函数需要成员函数公有,所以我们直接定义在类内,保证其封装性
//……类内
bool operator==(const Date& d2)
{
return _year == d2._year
&& _month == d2._month
&& _day == d2._day;
}
//……
2、赋值运算符重载
(1)格式
参数类型 const name& 引用传参
返回值类型 name& 返回引用
检测是否自己给自己赋值
返回*this
(2)赋值运算符的重载
赋值运算符只能重载成类的成员函数不能重载成全局函数
赋值运算符如果不显式实现,编译器会生成一个默认的,此时再在外边实现一个全局的赋值运算符重载,就会发生冲突
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date & d)
{
_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;
}
private:
int _year;
int _month;
int _day;
};
(3)用户没有显式实现时,编译器会生成一个默认赋值运算符重载,然后值拷贝,内置成员直接赋值,自定义成员需要调用对应类的赋值运算符重载完成赋值
(4)有了值拷贝我们就一定要说说深拷贝,在Date类这样的类中不需要我们自己实现,而在Stack这样的类中就需要显式实现,进行资源管理
拿出我们的老演员栈:
typedef int DataType;
class Stack
{
public:
Stack(size_t capacity = 10)
{
arr = (DataType*)malloc(capacity * sizeof(DataType));
if (nullptr == arr)
{
perror("malloc申请空间失败");
return;
}
size = 0;
capacity = capacity;
}
void Push(const DataType& data)
{
// CheckCapacity();
arr[size] = data;
size++;
}
~Stack()
{
if (arr)
{
free(arr);
arr = nullptr;
capacity = 0;
size = 0;
}
}
private:
DataType* arr;
size_t size;
size_t capacity;
};
int main()
{
Stack s1;
s1.Push(1);
s1.Push(2);
Stack s2;
s2 = s1;
return 0;
}
调试后发现又是析构函数这里出了问题,原因同拷贝构造函数:因为编译器自动生成的拷贝构造函数是值拷贝,所以在生成s2时,s2中的指针a指向的数组与s1中的指针指向的数组相同,在程序结束时,调用析构函数释放了s2,对应的这块数组空间也被释放,然后调用析构函数释放s1,已经被释放的空间不能被再次释放,所以出现了这样的错误,所以我们需要自己显式定义一个拷贝构造函数
3、前置++和后置++重载
我们先来复习一下前置++和后置++的区别,在仅自加时也就是在n++为一条语句时没有区别,在赋值时,前置++是先+1后赋值,后置++是先赋值再+1
如果我们想要++重载,那么就是定义operator++,分不出为前置还是后置,所以我们规定operator++为前置++,operator++(int)为后置++
class Date
{
public:
Date(int year = 1970, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
Date& operator++()
{
*this += 1;
return *this;
}
Date operator++(int)
{
Date tmp(*this);
*this += 1;
return tmp;
}
//所以后置++会使用空间拷贝,效率比前置++低
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d;
Date d1(2000,1,1);
d = d1++;
d.Print();
d1.Print();
d = ++d1;
d.Print();
d1.Print();
return 0;
}
六、const成员
被const修饰的成员函数称之为const成员函数,const实际修饰其中隐含的this指针,表明在该成员函数中不能对类内的任何成员进行修改
因为参数为隐藏的,所以我们的方法如下:
void Date::Print() const
{
cout << _year << "/" << _month << "/" << _day << endl;
}
相当于
void Date::Print(const Date* this)
{
cout << _year << "/" << _month << "/" << _day << endl;
}
注意:
const对象不能调用非const成员函数
非const对象能调用const成员函数
const成员函数内不能调用其他非const成员函数
非const成员函数内能调用其他const成员函数
七、日期类的实现
Date.h
#pragma once
#include <iostream>
using namespace std;
class Date
{
public:
int GetMonthDay(int year, int month);
Date(int year = 1970, int month = 1, int day = 1);
Date(const Date& d);
~Date();
Date& operator=(const Date& d);
Date& operator+=(int day);
Date& operator-=(int day);
Date& operator++();
Date operator++(int);
Date& operator--();
Date operator--(int);
Date operator+(int day);
Date operator-(int day);
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);
void Print() const;
private:
int _year;
int _month;
int _day;
};
Date.cpp
#define _CRT_SECURE_NO_WARNINGS
#include "Date.h"
int Date::GetMonthDay(int year, int month)
{
const static int days[13] = { 0,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 days[month];
}
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
if (month < 1 || month > 12
|| day < 1 || day > GetMonthDay(year, month))
{
cout << "非法日期" << endl;
exit(-1);
}
}
Date::Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
Date::~Date()
{
_year = _month = _day = 0;
}
//日期类的析构函数其实没必要写
Date& Date::operator=(const Date& d)
{
if(this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
Date& Date::operator+=(int day)
{
if (day < 0)
{
return *this -= (-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)
{
if (day < 0)
{
return *this += (-day);
}//复用+=
_day -= day;
while (_day <= 0)
{
--_month;
if (_month == 0)
{
--_year;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
Date& Date::operator++()
{
*this += 1;
return *this;
}
Date Date::operator++(int)
{
Date tmp(*this);
*this += 1;
return tmp;
}
Date& Date::operator--()
{
*this -= 1;
return *this;
}
Date Date::operator--(int)
{
Date tmp(*this);
*this -= 1;
return tmp;
}
Date Date::operator+(int day)
{
Date tmp(*this);
tmp += day;
return tmp;
}
Date Date::operator-(int day)
{
Date tmp(*this);
tmp -= day;
return tmp;
}
bool Date::operator==(const Date& d)
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
bool Date::operator<(const Date& d)
{
if (_year < d._year)
{
return true;
}
else if (_year == d._year && _month < d._month)
{
return true;
}
else if (_year == d._year && _month == d._month && _day < d._day)
{
return true;
}
else
{
return false;
}
}
// d1 <= d2
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);
}
int Date::operator-(const Date& d)
{
//假设左大右小,此时标识符flag为1
Date max = *this;
Date min = d;
int flag = 1;
//如果左小右大,则置换后置标识符flag为-1
if (*this < d)
{
max = d;
min = *this;
flag = -1;
}
//算出大的与小的之间的天数
int n = 0;
while (min != max)
{
++min;
++n;
}
//返回与标识符flag的乘积
return n * flag;
}
void Date::Print() const
{
cout << _year << "/" << _month << "/" << _day << endl;
}
test.cpp
#define _CRT_SECURE_NO_WARNINGS
#include "Date.h"
//初始化
void test1()
{
Date d1(2000, 1, 1);
d1.Print();
Date d2;
d2.Print();
Date d3 = Date(d1);
d3.Print();
}
//++
void test2()
{
Date d1(2001, 2, 28);
Date d2 = d1++;
Date d3 = ++d1;
}
//+ -
void test3()
{
Date d1(2000, 1, 1);
Date d2 = d1 + 20000;
Date d3 = d1 - 20000;
}
//--
void test4()
{
Date d1(2000, 3, 1);
Date d2 = d1--;
Date d3 = --d1;
}
//+= -=
void test5()
{
Date d1(2000, 1, 1);
d1 += 20000;
Date d2;
d2 -= 20000;
}
//日期-日期
void test6()
{
Date d1(2000, 1, 1);
Date d2;
int a = d1 - d2;
}
//=
void test7()
{
Date d1(2000, 1, 1);
Date d2;
d2 = d1;
}
int main()
{
//test1();
//test2();
//test3();
//test4();
//test5();
//test6();
//test7();
return 0;
}
test1测试结果
构造和拷贝构造函数正常
test2测试结果
前置后置++都正常
test3测试结果
+、 - 不改变原来的值,正常
test4测试结果
前置后置- -正常
test5测试结果
-= +=改变原来的数,正常
test6测试结果
日期减日期为整数正常
test7测试结果
=赋值正常
全部正常
八、关于取地址和const取地址操作符重载
&
const …… &
这两个一般不用重新定义,编译器会默认生成,如果有别的用途,可以显式定义重载
今日分享结束~