类和对象之运算符重载
Posted 可乐不解渴
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了类和对象之运算符重载相关的知识,希望对你有一定的参考价值。
文章目录
一.运算符重载
1.1 概念
在 C++语言中,可以用关键字
operator
加上运算符来表示函数,叫做运算符重载。
C++编译器在对运算符进行编译处理时,将一个运算符编译成以下形式:
例如两个复数相加函数:
Complex Add(const Complex &a, const Complex &b);
可以用运算符重载来表示:
Complex operator +(const Complex &a, const Complex &b);
1.2 运算符重载规则
1.2.1 运算符的重载规则
- ~重载后运算符的优先级与结合性不会改变。
- ~不能改变原运算符操作数的个数。
- ~不能重载C++中没有的运算符。
- ~不能改变运算符的原有语义。
1.2.2 可重载的运算符
在C++中的运算符除了以下5个运算符之外,其余全部可以被重载。
.
:成员选择运算符.*
:成员指针运算符::
:作用域分辨符?
:三目选择运算符sizeof
:计算数据大小运算符
从语法上讲,运算符既可以定义为全局函数,也可以定义为成员函数
运算符 | 规则 |
---|---|
所有的一元运算符 | 建议重载为成员函数 |
“= “、”() “、”[]”、" -> " | 只能重载为成员函数 |
“+=”、"-="、"/=" 、"*=" 、"&=" 、“l=” 、"~="、 “%=” 、">>=" 、"<<=" | 建议重载为成员函数 |
所有其它运算符 | 建议重载为全局函数 |
1.3 重载为类的友元函数
运算符之所以要重载为类的友元函数,是因为这样可以自由地访问该类中的任何数据成员。在类中声明类外定义友元函数的格式如下:
class name
{
friend 函数类型 operator 运算符(形参表);
};
函数类型 operator 运算符(形参表)
{
//函数体
}
示例:
class Person
{
friend ostream& operator<<(ostream& out, Person& p); //全局函数做友元
private:
//利用成员函数重载 左移运算符 p.operator<<(cout) 简化版本p<<cout
//不会利用成员函数重载<<运算符,因为无法实现 cout在左侧
int m_A;
int m_B;
};
//只能利用全局函数重载<<运算符
//ostream对象只能有一个
ostream& operator<<(ostream &out,Person &p) //本质 operator<<(cout,p) 简化cout<<p
{
cout << "m_A = " << p.m_A << " " << "m_B=" << p.m_B << endl;
return cout;
}
★总结
当运算符重载为类的友元函数时,函数参数个数与运算符的原操作数个数相同。函数中用到的数据(包括对象)均通过参数表传递。
1.4 重载为类的成员函数
将运算符函数重载为类的成员函数,这样运算函数就可以自由的访问本类中的所有的数据成员。
重载运算符函数为类的成员函数语法形式如下:
返回类型 类名::operator 运算符(形参表)
{
函数体;
}
其中:
- 类名是要重载该运算符的类,如果在类中定义运算符函数,类名与作用域分别符可以省略。
operator
与运算符构成运算符函数名。- 当运算符重载为类的成员函数时,函数的参数个数将比原来的操作数个数少一个,原因是通过对象调用该运算符函数时,对象本身充当了运算符函数最左边的操作数,少的操作数就是该对象本身。
示例
class Complex
{
public:
Complex(double real = 0.0, double image = 0.0)
{
this->real = real;
this->image = image;
}
Complex operator +(Complex B); //运算符 + 重载成员函数
private:
double real; //复数实部
double image; //复数虚部
};
//重载运算符 + 的函数实现
Complex Complex::operator+(Complex B)
{
//创建一个临时对象作为返回值,这里会调用拷贝构造函数
return Complex(this->real+B.real,this->image+B.image);
}
1.5 Date类的实现
光听光看,而不写是不可能完全领悟的,下面我们就以Date
类设计与实现为例:
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<iomanip>
#include<iostream>
//由于我们经常调用这个函数,且函数体较小
// 获取某年某月的天数
inline int GetMonthDay(int year, int month);
class Date
{
public:
// 全缺省的构造函数
Date(int year = 2000, int month = 1, int day = 1);
// 拷贝构造函数
// 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);
//前置++、--需要返回引用,后置++、--返回的不是引用,而是一个临时值,所以不能进行连续使用。
//后置--、++ 例如 a=b--; 我们是先把b原本的值给a,在让b自减1,而不是让它返回b的引用,返回b的引用会导致a与b的值相同
// 前置++
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);
// 打印日期
void PrintDate();
private:
int m_year; //年
int m_month; //月
int m_day; //日
};
#include"Date_class.h"
inline int GetMonthDay(int year, int month)
{
//这里创建一个元素个数为13个的数组,这样我们就可以把下标与月份一一对应上
static int DayArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31};
int day = DayArray[month];
if ((month == 2) && ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
{
day += 1;
}
return day;
}
//全缺省的构造函数
Date::Date(int year, int month, int day)
{
//检查初始化日期的值是否合法
if ((year >= 0) && ((month > 0) && (month < 13)) && (day <= GetMonthDay(year, month)))
{
this->m_day = day;
this->m_month = month;
this->m_year = year;
}
else
{
std::cout << "非法日期" << std::endl;
exit(-1);
}
}
//拷贝构造函数
Date::Date(const Date& d)
{
this->m_year = d.m_year;
this->m_month = d.m_month;
this->m_day = d.m_day;
}
//重载 = 号
Date& Date::operator=(const Date& d)
{
//有人可能想把自己对象的值赋给自己 例如 a=a;
if (this == &d)
{
return *this;
}
else
{
this->m_year = d.m_year;
this->m_month = d.m_month;
this->m_day = d.m_day;
return *this;
}
}
//析构函数
Date::~Date()
{
//由于我们并没有在堆区开辟空间,故不需要在析构函数中写任何代码
}
//重载 += 号
Date& Date::operator+=(int day)
{
if (day < 0)
{
//复用operator -=重载
return *this -= -day;
}
else
{
this->m_day += day;
while (this->m_day > GetMonthDay(this->m_year, this->m_month))
{
this->m_day -= GetMonthDay(this->m_year, this->m_month);
this->m_month++;
if (this->m_month > 12)
{
this->m_year++;
this->m_month = 1;
}
}
return *this;
}
}
//重载 + 号
Date Date::operator+(int day)
{
//由于+是两个数相加,对原来的对象不能有任何的改变,所以我们需要用值传递,返回一个临时变量
Date tempD(*this);
//复用operator +=重载
tempD += day;
return tempD;
}
//重载 - 号
Date Date::operator-(int day)
{
//由于-是两个数相减,对原来的对象不能有任何的改变,所以我们需要用值传递,返回一个临时变量
Date tempD(*this);
//复用operator -=重载
tempD -= day;
return tempD;
}
//重载 -= 号
Date& Date::operator-=(int day)
{
if (day < 0)
{
//复用operator +=重载
return this->operator+=(-day);
}
else
{
this->m_day -= day;
while (this->m_day <= 0)
{
this->m_month--;
if (this->m_month < 1)
{
this->m_year--;
this->m_month = 12;
}
this->m_day += GetMonthDay(this->m_year, this->m_month);
}
return *this;
}
}
//前置 ++
Date& Date::operator++()
{
*this += 1;
return *this;
}
//后置 ++
Date Date::operator++(int)
{
Date tempD(*this);
*this += 1;
return tempD;
}
//后置 --
Date Date::operator--(int)
{
Date tempD(*this);
*this -= 1;
return tempD;
}
//前置 --
Date& Date::operator--()
{
*this -= 1;
return *this;
}
//重载 != 号
bool Date::operator!=(const Date& d)
{
//复用==号重载
return !(*this==(d));
}
//重载 > 号
bool Date::operator>(const Date& d)
{
if (this->m_year > d.m_year)
{
return true;
}
else if (this->m_year == d.m_year)
{
if (this->m_month > d.m_month)
{
return true;
}
else if (this->m_month == d.m_month)
{
if (this->m_day > d.m_day)
{
return true;
}
}
}
return false;
}
//重载 == 号
bool Date::operator==(const Date& d)
{
if ((this->m_year == d.m_year) && (this->m_month == d.m_month) && (this->m_day == d.m_day))
{
return true;
}
else
{
return false;
}
}
//重载 >= 号
bool Date::operator>=(const Date& d)
{
//复用重载 == 号 和 >
return this->operator==(d) || this->operator>(d);
}
//重载 < 号
bool Date::operator<(const Date& d)
{
//复用重载的 >= 号
return !this->operator>=(d);
}
//重载 <= 号
bool Date::operator<=(const Date& d)
{
//复用重载的 >号
return !this->operator>(d);
}
//重载 - 号
int Date::operator-(const Date& d)
{
int flag = 1;
Date tempMax = *this;
Date tempMin = d;
if (tempMin < tempMax)
{
//利用模板中的swap函数来交换两个自定义的数据类型
std::swap(tempMax, tempMin);
flag = -1;
}
int ABS_day = 0;
while (tempMin < tempMax)
{
++tempMin;
++ABS_day;
}
return flag * ABS_day;
}
//打印日期
void Date::PrintDate()
{
std::cout << std::left << std::setw(4) << this->m_year << "年"
<< std::left << std::setw(2) << this->m_month << "月"
<< std::left << std::setw(2) << this->m_day << "日" << std::endl;
}
★总结:
- 双目运算符重载为类的成员函数时,函数只显式说明一个参数,该形参是运算符的右操作数。
- 前置单目运算符重载为类的成员函数时,不需要显式说明参数,即函数没有形参。
- 后置单目运算符重载为类的成员丽数时,为了与前置单目运算符相区别,函数要带有一个整型形参。
END...
以上是关于类和对象之运算符重载的主要内容,如果未能解决你的问题,请参考以下文章
类和对象——补充(运算符重载,static和explicit关键字和友元,内部类)