【C++】类和对象(5)--运算符重载

目录

[一 概念](#一 概念)

[二 运算符重载的实现](#二 运算符重载的实现)

[三 关于时间的所有运算符重载](#三 关于时间的所有运算符重载)

[四 默认赋值运算符](#四 默认赋值运算符)

[五 const取地址操作符重载](#五 const取地址操作符重载)


一 概念

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。

函数名字为:关键字operator后面接需要重载的运算符符号。

函数原型:返回值类型 operator操作符(参数列表)

注意:

不能通过连接其他符号来创建新的操作符:比如operator@

重载操作符必须有一个类类型参数

用于内置类型的运算符,其含义不能改变,例如:内置的整型 + ,不能改变其含义

作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this

总之如何去比较自定义类型, 并且要有可读性, 那就需要运算符重载

二 运算符重载的实现

cpp 复制代码
// 全局的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 Test1()
{
	Date d1(2018, 9, 26);
	Date d2(2018, 9, 27);
	/*cout << operator>(d1, d2) << endl;
	  cout << operator==(d1, d2) << endl;*/

	cout << (d1 == d2) << endl;
}

int main()
{
	Test1();
	return 0;
}

现在我们把==运算符重载到成员函数中

cpp 复制代码
// 全局的operator==
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	bool operator==(const Date& dd2)
	{
		return _year == dd2._year
			&& _month == dd2._month
			&& _day == dd2._day;      
	}


private:
	int _year;
	int _month;
	int _day;
};

void Test2()
{
	Date d1(2018, 9, 26);
	Date d2(2018, 9, 27);
	/*cout << operator>(&d1, d2) << endl;
	  cout << operator==(&d1, d2) << endl;*/
	cout << (d1 == d2) << endl;
}

int main()
{
	Test2();
	return 0;
}

总结:

运算符重载 函数重载 他们之间没有关联
运算符重载:自定义类型可以直接使用运算符

函数重载:可以允许参数不同的同名函数,

内置类型对象可以直接用各种运算符,内置类型都是简单类型

语言自己定义,编译直接转换成指令

自定义类型呢?不支持 所以运算符重载诞生
不能被重载的运算符只有5个, 点号.三目运算 ? : 作用域访问符::运算符sizeof 以及.*(*是可以重载的 只是点星是不能的)

三 关于时间的所有运算符重载

1 Date.h

cpp 复制代码
#pragma once

#include<iostream>
#include<assert.h>
using namespace std;

class Date
{
public:
       //全缺省参数只需要在声明中
       Date(int year = 1, int month = 1, int day = 1);

       void Print();
       int GetMonthDay(int year, int month);

       Date& operator=(const Date& d);
       bool operator==(const Date& y);
       bool operator!=(const Date& y);
       bool operator>(const Date& y);
       bool operator<(const Date& y);
       bool operator>=(const Date& y);
       bool operator<=(const Date& y);

       int operator-(const Date& d);
       Date& operator+=(int day);
       Date operator+(int day);
       Date& operator-=(int day);
       Date operator-(int day);

       Date& operator++();
       Date operator++(int);

       Date& operator--();
       Date operator--(int);
  
      //友元函数
       friend ostream& operator<<(ostream& out, const Date& d);
       friend istream& operator>>(istream& in, Date& d);

private:
       int _year;
       int _month;
       int _day;
};

ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);

2 Date.cpp

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"


int Date:: GetMonthDay(int year, int month)
{
       int days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
       int day = days[month];
       if (month == 2 && (year % 4 == 0 && year % 100 != 0) && (year % 100 == 0))
       {
              day += 1;
       }

       return day;
}

//构造函数
Date:: Date(int year, int month, int day)
{
       _year = year;
       _month = month;
       _day = day;
}

void Date::Print()
{
       cout << _year << "/" << _month << "/" << _day << endl;
}

//赋值运算符
Date& Date::operator=(const Date& d)
{
       if (*this != d)
       {
              _year = d._year;
              _month = d._month;
              _day = d._day;
       }

       return *this;
}
//赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,
//就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数

bool Date::operator==(const Date& d)
{
       return (_day == d._day && _month == d._month && _year == d._year);
}

bool Date::operator!=(const Date& d)
{
       return !(*this == d);
}

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;
       }

}

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);
}


//在类里面是不用区分函数顺序的
Date& Date::operator+=(int day)
{
       if (day < 0)
       {
              return *this -= (-day);
       }
       _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)
{
       if (day < 0)
       {
              *this += (-day);
       }

       _day -= day;
       while (_day < 0)
       {
              --_month;
              if (_month == 0)
              {
                      --_year;
                      _month = 12;
              }
              _day += GetMonthDay(_year, _month);
       }
       return *this;
}


Date Date:: operator-(int day)
{
       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;
}

//--d1
Date& Date::operator--()
{
       *this -= 1;
       return *this;
}

//d1--
Date Date::operator--(int)
{
       Date tmp(*this);
       *this -= 1;
       return tmp;
}

//d1 -100
int Date::operator-(const Date& d)
{
       //假设左大右小
       int flag = 1;
       Date max = *this;
       Date min = d;
       if (*this < d)
       {
              flag = -1;
              min = *this;
              max = d;
       }
       int n = 0;
       while (min != max)
       {
              min++;
              n++;
       }
       return n * flag;
}


ostream& operator<<(ostream& out, const Date& d)
{
       out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
       return out;
}

istream& operator>>(istream& in, Date& d)
{
       in >> d._year >> d._month >> d._day;
       return in;
}

3 Test.cpp

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include "Date.h"

void TestDate1()
{
       Date d1(2023, 10, 24);
       d1.Print();

       Date ret1 = d1 - 100;
       ret1.Print();

       Date ret2 = d1 - 10000;
       ret2.Print();

       Date ret3 = d1 + 100;
       ret3.Print();

       Date ret4 = d1 + 10000;
       ret4.Print();
}

void TestDate2()
{
       Date d1(2023, 10, 24);
       d1.Print();

       // 语法设计,无法逻辑闭环,那么这时就只能特殊处理
       // 特殊处理
       ++d1;
       d1.operator++();
       d1.Print();

       d1++;
       d1.operator++(10);
       d1.operator++(1);
       d1.Print();
}

void TestDate3()
{
       Date d1(2023, 10, 24);
       d1.Print();

       Date d2(2024, 5, 5);
       d2.Print();

       Date d3(2024, 8, 1);
       d3.Print();

       cout << d2 - d1 << endl;
       cout << d1 - d3 << endl;

}

void TestDate4()
{
       Date d1(2023, 10, 24);
       d1 += -100;

       d1.Print();
}

void Test5()
{
       Date d1(2023, 10, 21);
       Date d2(2023, 12, 31);
       d1.Print();
       cout << d1;
       cin >> d2;
       cout << d2 << d1 << endl;
}


int main()
{
       TestDate1();
       TestDate2();
       TestDate3();
       TestDate4();
       Test5();

       return 0;
}

解释一下<< >>运算符重载

四 默认赋值运算符

用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注 意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符 重载完成赋值。

Date MyStack这些就不用自己构造赋值运算符重载, 但是栈这些就必须要自己构造, 因为涉及到了资源的拷贝

注意:如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必 须要实现。

cpp 复制代码
// 这里会发现下面的程序会崩溃 这里就需要我们以后讲的深拷贝去解决。
typedef int DataType;
class Stack
{
public:
       Stack(size_t capacity = 10)
       {
              _array = (DataType*)malloc(capacity * sizeof(DataType));
              if (nullptr == _array)
              {
                      perror("malloc申请空间失败");
                      return;
              }
              _size = 0;
              _capacity = capacity;
       }
       void Push(const DataType& data)
       {
              // CheckCapacity();
              _array[_size] = data;
              _size++;
       }
       ~Stack()
       {
              if (_array)
              {
                      free(_array);
                      _array = nullptr;
                      _capacity = 0;
                      _size = 0;
              }
       }
private:
       DataType* _array;
       size_t _size;
       size_t _capacity;
};
int main()
{
       Stack s1;
       s1.Push(1);
       s1.Push(2);
       s1.Push(3);
       s1.Push(4);
       Stack s2;
       s2 = s1;
       return 0;
}

改正如下

cpp 复制代码
typedef int DataType;
class Stack
{
public:
       Stack(size_t capacity = 10)
       {
              _array = (DataType*)malloc(capacity * sizeof(DataType));
              if (nullptr == _array)
              {
                      perror("malloc申请空间失败");
                      return;
              }
              _size = 0;
              _capacity = capacity;
       }
       void Push(const DataType& data)
       {
              // CheckCapacity();
              _array[_size] = data;
              _size++;
       }

       void Pop()
       {
              _size--;
       }

       DataType Top()
       {
              return _array[_size - 1];
       }

       bool Empty()
       {
              return _size == 0;
       }


       Stack& operator=(Stack& st)
       {
              _array = (int*)malloc(sizeof(int) * st._capacity);
              if (_array == nullptr)
              {
                      perror("malloc fail");
                      exit(-1);
              }
              memcpy(_array, st._array, sizeof(int) * st._size);
              _size = st._size;
              _capacity = st._capacity;
       }


       ~Stack()
       {
              if (_array)
              {
                      free(_array);
                      _array = nullptr;
                      _capacity = 0;
                      _size = 0;
              }
       }
private:
       DataType* _array;
       size_t _size;
       size_t _capacity;
};
int main()
{
       Stack s1;
       s1.Push(1);
       s1.Push(2);
       s1.Push(3);
       s1.Push(4);
       while (!s1.Empty())
       {
              printf("%d ", s1.Top());
              s1.Pop();
       }
       printf("\n");

       Stack s2;
       s2 = s1;
       s2.Push(5);
       s2.Push(6);
       s2.Push(7);
       s2.Push(8);
       while (!s2.Empty())
       {
              printf("%d ", s2.Top());
              s2.Pop();
       }
       return 0;
}

讲一下为什么

五 const取地址操作符重载

这两个默认成员函数一般不用重新定义 ,编译器默认会生成。

cpp 复制代码
       Date* operator&() //返回类型为 Date*
       {
              cout << "Date* operator&()" << endl;
              return this;
       }

       const Date* operator&()const//返回类型为 const Date*
       {
              cout << "const Date* operator&()const" << endl;
              return this;
       }

当然不是const的地址也可以调用const类型, 只不过两个都存在的时候, 会优先调用最匹配的一个

这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需 要重载,比如想让别人获取到指定的内容!

本节感觉理解起来还是比较抽象, 有时候记住咋用就行了, 祖师爷就是这样规定的, 但是底层的东西我们还是得好好琢磨一下.对于类和对象基础要求挺高. 大家可以看看我之前的博客.

继续加油!

相关推荐
yyycqupt21 分钟前
数据库连接池(二)
linux·数据库·c++·后端·单例模式
苏言の狗44 分钟前
CCF认证202406-01 | 矩阵重塑(其一)
c语言·数据结构·c++·算法·矩阵
_GR1 小时前
每日OJ_牛客_游游的字母串_枚举_C++_Java
java·数据结构·c++·算法·哈希算法
花糖纸木2 小时前
进程控制(详解)
linux·服务器·c++
03羊2 小时前
右值引用和移动语义
c++
Stanford_11062 小时前
用c++做游戏开发至少要掌握哪些知识?
开发语言·c++·微信小程序·c·微信公众平台·twitter·微信开放平台
澄澈i2 小时前
设计模式学习[9]---模板方法模式
c++·学习·设计模式·模板方法模式
沃和莱特3 小时前
C++中类的继承
数据库·c++·编程·c·指针·友元函数
一只鸡某3 小时前
实习冲刺第二十九天
数据结构·c++·算法·leetcode
鲨鱼吃橘子3 小时前
【C++融会贯通】哈希表的使用
c语言·开发语言·数据结构·c++·链表·哈希算法·散列表