1 日期类包含所学的四种函数
#include <iostream>
using namespace std;
#if 0
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
~Date()
{}
//左操作数是this指向的调用函数的对象
bool operator==(const Date& d)
{
return _year == d._year &&
_month == d._month &&
_day == d._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2020, 10, 14);
Date d2(2020, 10, 15);
if (d1 == d2)
{
;
}
return 0;
}
#endif
如果不写==赋值运算符重载,d1 == d2 就报错。
2 注意
2.1 不能通过连接其他符号来创建新的操作符:比如operator@
// 注意:不能臆造运算符来进行重载
void operator@()
{
}
@下面报错
2.2 重载操作符必须有一个类类型或者枚举类型的操作数
int operator+(int left, int right)
{
return left + right;
}
上述代码中,重载操作符都是类置类型int型,并且还是无限递归,所以错误。再怎么重载也不可能重载出其他的运算符
2.3 用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不 能改变其含义
#if 0
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
~Date()
{}
bool operator==(const Date& d)
{
return _year == d._year &&
_month == d._month &&
_day == d._day;
}
Date& operator+(int days)
{
_day += days;
return *this;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2020, 10, 14);
Date d2(2020, 10, 15);
d2 = d1 + 10;
//d1值也应该不变,注意该处的d1传过去类似于this当前对象,所以形参应该是int days
int a = 10;
int b = 20;
int c = 0;
c = a + b;
//此时a,b值不变
return 0;
}
#endif
上述该代码:看似没错,但对于+赋值操作符函数,其改变了原先的d1值。
其实应该是对+=进行赋值操作符函数。
修改代码:
Date& operator+(int days)
{
_day += days;
return *this;
}
再举个例子,本来是加法运算,写成减法的逻辑。
代码如下:
//此处this相当于是d1
Date operator+(/*Date* const this,*/int days)
{
Date temp(*this);
temp._day -= days;
return temp;
}
实际应该是:
Date operator+(/*Date* const this,*/int days)
{
Date temp(*this);
temp._day += days;
return temp;
}
2.4 作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的操作符有一个默认的形参this,限定为第一个形参
#if 0
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
~Date()
{}
//类中时可以省略
Date operator+(/*Date* const this,*/int days)
{
Date temp(*this);
temp._day += days;
return temp;
}
private:
int _year;
int _month;
int _day;
};
//全局时,需要写全。
Date operator-(const Date& d, int days)
{
Date temp(d);
temp._day -= days;//如果_day是类中私有成员,这块不能访问,此时将其改为公有的。
return temp;
}
int main()
{
Date d1(2020, 10, 14);
Date d2(2020, 10, 15);
d2 = d1 - 1;
return 0;
}
#endif
第一种:类中时可以省略,省略了Date* const this
Date operator+(/*Date* const this,*/int days)
{
Date temp(*this);
temp._day += days;
return temp;
}
第二种:全局时,需要将参数写全。
Date operator-(const Date& d, int days)
{
Date temp(d);
temp._day -= days;//如果_day是类中私有成员,这块不能访问,此时将其改为公有的。
return temp;
}
2.5 5个运算符不能重载。
.*
::
sizeof
?:
.
3 赋值运算符
#if 0
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
~Date()
{}
// =赋值运算符函数应注意:
//1、返回值
//2、参数类型
//3、检测是否自己给自己赋值
//4、返回*this
//5、一个类如果没有显式定义赋值运算符重载,编译器也会生成一个,完成对象按字节序的值拷贝。
Date& operator=(const Date& d)
{
// this是=运算符的左操作数,d是=运算符的右操作数
//cout << this << "=" << &d << endl;
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
//return d;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2020, 10, 14);
Date d2(2020, 10, 15);
Date d3(2020, 10, 16);
//1、d1 = d2 等价===》d1.operator=(d2)
d1 = d2;
d1.operator=(d2);
//d1 = d2 = d3 等价===》d1.operator=(d2.operator=(d3));
d1 = d2 = d3;
//2、自己给自己赋值没有意义,但是有时写成引用的形式,会没有看到
d1 = d1;
Date& d4 = d1;
d4 = d1;
//3、变量赋值
//连续赋值-->其返回值是什么
int a = 10;
int b = 20;
int c = 0;
a = b;
a = c;
a = b = c;
return 0;
}
#endif
3.1 参数问题:引用还是传值
void operator=(const Date d)
{
cout << this << "=" << &d << endl;
}
int main()
{
Date d1(2020, 10, 14);
Date d2(2020, 10, 15);
d1 = d2;
return 0;
}
调试时,往里面走一步,发现其d1 = d2 ,调用的是拷贝构造函数。
原因:参数是按照值的方式进行传递。按照值的方式进行传递,会在传参期间生成一份拷贝。拷贝一个新的对象,这种传参效率太低了。解决这种问题,对其加引用,引用就相当于别名。d就相当于是实参的别名。
修改后代码如下:
void operator=(const Date& d)
{
// this是=运算符的左操作数,d是=运算符的右操作数
cout << this << "=" << &d << endl;
}
运行结果可以看出:将d2的地址给d1了。
最终结果:引用
void operator=(const Date& d)
{
// this是=运算符的左操作数,d是=运算符的右操作数
//cout << this << "=" << &d << endl;
_year = d._year;
_month = d._month;
_day = d._day;
}
3.2 返回值:无返回值还是Date& 还是Date
int main()
{
//c给b赋值,然后b给a赋值
a = b;
a = c;
a = b = c;
}
变量可以进行连续赋值,那对象呢?
void operator=(const Date& d)
{
// this是=运算符的左操作数,d是=运算符的右操作数
//cout << this << "=" << &d << endl;
_year = d._year;
_month = d._month;
_day = d._day;
}
int main()
{
Date d1(2020, 10, 14);
Date d2(2020, 10, 15);
Date d3(2020, 10, 16);
d1 = d2;
d1.operator=(d2);
d1 = d2 = d3; // ===》d1.operator=(d2.operator=(d3));
return 0;
}
不支持连续赋值。
d1 = d2;
d1.operator=(d2);
两个代码是等价的,反汇编代码如下:
d1 = d2 = d3;
d1.operator=(d2.operator=(d3));
因此:返回值类型应该是个日期类型:Date,但是,应该是Date& 还是 Date
- 一般,设计函数期间,返回值类型还是参数,涉及到自定义类型时,能先用Date&,先使用Date&.如果不用引用,有可能调用的时拷贝构造函数,在进入赋值运算符函数。
最终结果:Date&
Date& operator=(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
3.3 返回值问题:return *this还是return d
Date& operator=(const Date& d)
{
// this是=运算符的左操作数,d是=运算符的右操作数
//cout << this << "=" << &d << endl;
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
return d;
}
int main()
{
Date d1(2020, 10, 14);
Date d2(2020, 10, 15);
Date d3(2020, 10, 16);
//1、d1 = d2 等价===》d1.operator=(d2)
d1 = d2;
d1.operator=(d2);
//d1 = d2 = d3 等价===》d1.operator=(d2.operator=(d3));
d1 = d2 = d3;
return 0;
}
不管返回是return *this还是return d,都会达到预期的效果。
但是,哪个效果到底更好一些呢?
针对这个语句
d1 = d2 = d3
d1.operator=(d2.operator=(d3));
d3传过去给d,d2相当于this,所以结果是返回return *this;更好。
*最终结果:return this;
Date& operator=(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
2.4 检测是否自己给自己赋值
#if 0
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
~Date()
{
}
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;
};
int main()
{
Date d1(2020, 10, 14);
//自己给自己赋值没有意义,但是有时写成引用的形式,会没有看到
d1 = d1;
Date& d4 = d1;
d4 = d1;
return 0;
}
结论:自己给自己赋值没有意义,但是有时写成引用的形式,会没有看到,所以添加一个判断。
4 赋值运算符函数注意的点
=赋值运算符函数应注意:
- 返回值
- 参数类型
- 检测是否自己给自己赋值
- 返回*this
- 一个类如果没有显式定义赋值运算符重载,编译器也会生成一个,完成对象按字节序的值拷贝。
Date& operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
5 ++ – 运算符函数
知识点7:++ -- 运算符重载
#if 0
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
~Date()
{}
// 前置++
Date& operator++()
{
_day++;
return *this;
}
// 后置++
// 先使用 后加+1
Date operator++(int)
{
Date temp(*this);
_day++;
return temp;
}
Date& operator--()
{
_day--;
return *this;
}
// 后置--
// 先使用 后-1
Date operator--(int)
{
Date temp(*this);
_day--;
return temp;
}
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2020, 10, 14);
Date d2(2020, 10, 15);
//对象
d2 = d1++; // 后置++
d2 = ++d1; // 前置++
//变量
int a = 10;
a++;
a--;
return 0;
}
#endif
5.1 前置++
Date& operator++()
{
_day++;
return *this;
}
5.2 后置++
Date operator++(int)
{
Date temp(*this);
_day++;
return temp;
}
5.3 前置–
Date& operator--()
{
_day--;
return *this;
}
5.4 后置–
Date operator--(int)
{
Date temp(*this);
_day--;
return temp;
}
- 形参部分基本都是引用:不想通过外部实参改变形参,加上const就可以。
- 返回类型,一般先加引用,看返回实体,如果是栈上的实体,就去掉引用。
5 & 运算符函数
#if 0
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
Date* operator&()
{
cout << this << endl;
return this;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2020, 10, 14);
//没有实现&赋值运算符函数时,打印地址
cout << &d1 << endl;
//写一个&赋值运算符函数,见到&符号,就打印地址
&d1;
//不打印地址,此时因为是const修饰。
const Date d2(2020, 10, 15);
&d2;
return 0;
}
#endif
往期链接:
cpp_9.3类和对象中(九) — 赋值运算重载符
cpp_9.2类和对象中(八) — 拷贝构造函数
cpp_9.1 类和对象中(七)—构造函数中,无参对象如何调用?底层如何实现?
cpp_8.2类和对象中(六) — 析构函数
cpp_8.1类和对象中(五) — 构造函数链接描述
cpp_8 类和对象上(四) — this遗留
cpp_7.1 类和对象上(三) — this
cpp_7类与对象上(二) — 类对象的存储方式
cpp_6.1类与对象上(一)— 类的引入