目录
5.赋值运算符重载
5.1 运算符重载
5.2 赋值运算符重载
5.3 前置++和后置++重载
5.4 重载流插入与流提取
流插入<<运算符重载
流提取运算符重载
6.日期类实现
7.const成员
8.取地址 及 const取地址操作符 重载
5.赋值运算符重载
5.1 运算符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型operator操作符(参数列表)
注意:
- 不能通过连接其他符号来创建新的操作符:比如operator@
- 重载操作符必须有一个类(的)类型参数
- 用于内置类型的运算符,其含义不能改变,例如:内置类型+,不能改变其含义
- 作为类成员函数重载时,其形参看起来比操作数 数目少1,因为成员函数的第一个参数为隐藏的this。
5. .* :: sizeof ?: . 注意以上5个运算符不能重载,这个经常在笔试选择题中出现
6. 对于运算符重载,显示调用与转换调用语法实现效果是一样的
7. 当将函数重载成全局的时候,就无法访问类内私有成员,解决方案如下:
- 提供这些成员get和set(Java爱用)
- 友元
- 重载为成员函数(一般使用这种)
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
int GetYear()
{
return _year;
}
// //实现d1 == d2 重载为成员函数
// bool operator==(const Date& d){
// return this->_year == d._year && this->_month == d._month &&
// this->_day == d._day;
// }
public:
int _year;
int _month;
int _day;
};
// 重载成全局,无法访问私有成员
// 1、提供这些成员get和set
// 2、友元 后面会讲
// 3、重载为成员函数(一般使用这种)
bool operator==(const Date& d1, const Date& d2)
{
return d1._year == d2._year
&& d1._month == d2._month
&& d1._day == d2._day;
}
//d1-d2
//d1+d2 没有意义
//d1*d2 没有意义
//一个类要重载哪些运算符是看需求,看重载有没有价值和意义
int main()
{
Date d3(2024, 4, 14);
Date d4(2024, 4, 15);
// 显式调用
operator==(d3, d4);
// 直接写,装换调用,编译会转换成operator==(d3, d4);
d3 == d4;
return 0;
}
5.2 赋值运算符重载
- 赋值运算符重载格式
a. 参数类型:const T&,传递引用可以提高传参效率
b. 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
c. 检测是否自己给自己赋值
d. 返回*this:要复合连续赋值的含义
2.赋值运算符重载函数必须作为成员函数,不能作为全局函数。并且函数出了类域,将不在有this指针的特性,此时需要给两个参数
原因:赋值运算符如果不显示实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数
3.用户没有显示实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值
注意:如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理必须要实现。比如下面这个例子
5.3 前置++和后置++重载
class Date{
private:
int _year;
int _month;
int _day;
public:
Date(int year = 2024,int month = 4,int day = 17){
_year = year;
_month = month;
_day = day;
}
//前置++:返回+1之后的结果
//注意:this指向的对象函数结束后不会销毁,故以引用方式返回提高效率
Date& operator++(){
_day +=1;
return *this;
}
//后置++
//前置++和后置++都是一元运算符,为了让前置++和后置++能形成正确重载
// C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器
//自动传递
// 注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存
//一份,然后给this+1
Date operator++(int){
Date tmp = (*this);
_day += 1;
return tmp;//注意:tmp是临时对象,因此只能以值的方式返回,不能返回引用
}
};
int main(){
Date d; //d: 2024 4 17
Date d1(2000,1,1); //d1: 2000,1,1
d = d1++;//d : 2000,1,1 d1: 2000,1,2
d = ++d1;//d ; 2000,1,3 d1: 2000,1,3
return 0;
}
5.4 重载流插入与流提取
在C++标准库中,cout和cin都是iostream中ostream 和istream 的对象,对于流插入<<运算符,之所以cout输出可以不用指定占位符编译器可以自动匹配的原因是ostream中<<的运算符重载,对于内置类型来说,有下面的函数重载
流插入<<运算符重载
我们来实现流插入重载
在上面代码中,虽然重载了<<,但是形参是ostream流的对象,而隐含的形参是this,而在运算符重载函数形参列表规则中,对于有两个操作数的运算符重载来说,这里第一个参数为左操作数(隐含的this),第二个参数为右操作数,所以上面代码应改为d<<cout;
为了能够实现和流插入一样的效果,我们就要进行修改,但this所在的形参是更改不了的,因此我们将<<重载放到全局中(全局中失去了this的特性),此时可以决定两个操作数的顺序。
由于我们将重载放在了全局中,那么全局无法访问类中私有成员,这里我们使用友元的方法,解除私有问题。
流提取运算符重载
class Date
{
friend ostream& operator<<(ostream &cout,Date& d);
friend istream& operator>>(istream &cin ,Date& d);
private:
int _day;
int _month;
int _year;
public:
Date(int year = 2024, int month = 4, int day = 18)
{
_year = year;
_month = month;
_day = day;
}
//运算符重载中,参数顺序和操作顺序是一致的
// void operator<<(ostream &cout){
// cout << _year << "-" << _month << "-" << _day <<endl;
// }
};
//使用返回值类型 可以进行连续插入
ostream& operator<<(ostream &cout,Date& d){
cout << d._year << "-" << d._month << "-" << d._day <<endl;
return cout;
}
istream& operator>>(istream &cin ,Date& d){
cin >> d._year >> d._month >> d._day;
return cin;
}
int main()
{
Date d(1999,8,1);
Date d1;
cin >> d >> d1;
return 0;
}
6.日期类实现
//
// Date.hpp
// 类和对象2⃣️
//
// Created by 南毅 on 2024/4/19.
//
#pragma once
#include<iostream>
using namespace std;
#include<assert.h>
class Date
{
// 友元函数声明
public:
Date(int year = 1900, int month = 1, int day = 1);
void Print() const;
// 直接定义类里面,他默认是inline
// 频繁调用
int GetMonthDay(int year, int month)
{
assert(month > 0 && month < 13);
static int monthDayArray[13] = { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
// 365天 5h +
if (month == 2 && (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
{
return 29;
}
else
{
return monthDayArray[month];
}
}
bool CheckDate();
bool operator<(const Date& d) const;
bool operator<=(const Date& d) const;
bool operator>(const Date& d) const;
bool operator>=(const Date& d) const;
bool operator==(const Date& d) const;
bool operator!=(const Date& d) const;
// d1 += 100
Date& operator+=(int day);
Date operator+(int day) const;
// d1 -= 100
Date& operator-=(int day);
// d1 - 100;
Date operator-(int day) const;
// d1 - d2
int operator-(const Date& d) const;
// ++d1 -> d1.operator++()
Date& operator++();
// d1++ -> d1.operator++(1)
// 为了区分,构成重载,给后置++,强行增加了一个int形参
// 这里不需要写形参名,因为接收值是多少不重要,也不需要用
// 这个参数仅仅是为了跟前置++构成重载区分
Date operator++(int);
// 15:55
Date& operator--();
Date operator--(int);
private:
int _year;
int _month;
int _day;
};
//
// Date.cpp
// 类和对象2⃣️
//
// Created by 南毅 on 2024/4/19.
//
#include "Date.hpp"
Date::Date(int year, int month, int day){//缺省值在.h文件显示
_year = year;
_month = month;
_day = day;
if(!CheckDate()){
perror("日期非法\n");
}
}
void Date::Print() const{
cout<< _year << "-" << _month << "-" << _day <<endl;
}
bool Date::CheckDate(){
if(_month > 12 || _month < 1 || _day > GetMonthDay(_year, _month)||_day<1){
return false;
}else{
return true;
}
}
//d1 < d2
bool Date::operator<(const Date& d) const{
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;
}
//d1 == d2
bool Date::operator==(const Date& d) const{
return _year == d._year && _month == d._month && _day == d._day;
}
//d1 <= d2
bool Date::operator<=(const Date& d) const{
return (*this < d) || (*this == d);
}
//d1 > d2
bool Date::operator>(const Date& d) const{
return !(*this <= d);
}
//d1 >= d2
bool Date::operator>=(const Date& d) const{
return !(*this < d);
}
//d1 != d2
bool Date::operator!=(const Date& d) const{
return !(*this == d);
}
//d1 += 50
Date& Date::operator+=(int day){
_day += day;
while(_day > GetMonthDay(_year, _month)){
_day -= GetMonthDay(_year, _month);
_month++;
while (_month > 12) {
_year += 1;
_month =1 ;
}
}
return *this;
}
Date Date::operator+(int day) const{
Date tmp = *this;
tmp += day;
return tmp;
}
// d1 -= 50;
Date& Date::operator-=(int day){
while (_day - day < 0) {
_month--;
while (_month < 1) {
_year--;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
_day -= day;
return *this;
}
//d1 - 100
Date Date::operator-(int day) const
{
Date tmp = *this;
tmp -= day;
return tmp;
}
//++d1
Date& Date::operator++()
{
*this += 1;
return *this;
}
// d1++
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;
}
// d1 - d2
int Date::operator-(const Date& d) const{
Date max = *this;
Date min = d;
int flag = 1;
int n = 0;
if((*this) < d){
max = d;
min = *this;
flag = -1;
}
while (max != min) {
max--;
n++;
}
return n * flag;
}
7.const成员
将const修饰的“成员函数“称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改
8.取地址 及 const取地址操作符 重载
这两个默认成员函数一般不用重新定义,编译器默认会生成。
class A
{
public:
// 我们不实现,编译器会自己实现,我们实现了编译器就不会自己实现了
// 一般不需要我们自己实现
// 除非不想让别人取到这个类型对象的真实地址
A* operator&()
{
cout << "A* operator&()" << endl;
return nullptr;
}
const A* operator&() const
{
cout << "const A* operator&() const" << endl;
return (const A*)0xffffffff;
}
private:
int _a1 = 1;
int _a2 = 2;
int _a3 = 3;
};
int main()
{
A aa1;
const A aa2;
cout << &aa1 << endl;
cout << &aa2 << endl;
return 0;
}