【C++从练气到飞升】05---运算符重载(一)

简介: 【C++从练气到飞升】05---运算符重载(一)

🎈个人主页库库的里昂

收录专栏C++从练气到飞升

🎉鸟欲高飞先振翅,人求上进先读书

一、运算符重载的引用

本章将以日期类为例进行展开叙述

通常比较两个操作数的大小,会写成下述方式:

int main()
{
  int i = 1, j = 2;
  i < j;
  return 0;
}

但是对于日期类采用上述方式会发生报错:

class Date
{
public:
  Date(int year = 2023, int month = 9, int day = 25)
  {
    _year = year;
    _month = month;
    _day = day;
  }
  void Print()
  {
    cout << _year << "/" << _month << "/" << _day << endl;
  }
private:
  int _year;
  int _month;
  int _day;
};
int main()
{
  Date d1(2023, 10, 1);
  Date d2(2022, 2, 15);
  d1.Print();
  d2.Print();
  
  d1 < d2;
  
  return 0;
}

🌟因为日期类是我们自己定义的,属于一种自定义类型,它的大小比较方式,编译器是不知道的,而像int等内置类型,这是原生语言定义的,知道规则来比较,可以直接用<、>去比较两个内置类型变量的大小,但自定义类型不可以哦

最常见的解决方式:

🌟创建一个函数来通过一 一对比年月日来实现日期类的比较,不过会出现关于私有的问题,这个函数是写在类外面的,意味着,日期类的成员变量是private私有的,在类外面就无法访问,所以在这个函数里面是访问不到类对象中的_year、_month、_day,所以x1._year等都是错误的会发生报错,要想实现该函数的功能,可以采用下面3种方法:

class Date
{
public:
  Date(int year = 2023, int month = 9, int day = 25)
  {
    _year = year;
    _month = month;
    _day = day;
  }
  void Print()
  {
    cout << _year << "/" << _month << "/" << _day << endl;
  }
//private:
  int _year;
  int _month;
  int _day;
};
bool DateLess(const Date& x1, const Date& x2)
{
  if (x1._year < x2._year)
  {
    return true;
  }
  else if (x1._year == x2._year && x1._month < x2._month)
  {
    return true;
  }
  else if (x1._year == x2._year && x1._month == x2._month&&x1._day<x2._day)
  {
    return true;
  }
  else
  {
    return false;
  }
}
int main()
{
  Date d1(2023, 10, 1);
  Date d2(2022, 2, 15);
  d1.Print();
  d2.Print();
  DateLess(d1, d2);
  return 0;

🌟但是对于这种函数写法相比于直观的<、>来看显然不是很直观,对于不清楚的other来说,读代码是很困难的,所以就引入了运算符重载

二、运算符重载

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

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

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

bool operator<(const Date& x1, const Date& x2)
{
  if (x1._year < x2._year)
  {
    return true;
  }
  else if (x1._year == x2._year && x1._month < x2._month)
  {
    return true;
  }
  else if (x1._year == x2._year && x1._month == x2._month&&x1._day<x2._day)
  {
    return true;
  }
  else
  {
    return false;
  }
}
int main()
{
  Date d1(2023, 10, 1);
  Date d2(2022, 2, 15);
  
  //cout<<d1<d2<<endl;
  至于为什么不写成cout<<d1<d2<<endl是因为<<的优先级高于<
  
  d1<d2与operator<(d1<d2)本质上都是调用运算符重载,所以两者写法是等价的只不是一个显示调用,一个没有显示调用
  
  cout << (d1 < d2) << endl;
  cout << (operator<(d1, d2)) << endl;
  
  return 0;
}

🌟上述代码就是对<运算符的一个重载,此时两个日期类对象就可以直接用<来比较大小,d1 < d2本质上就是调用运算符重载函数,此外,还需要解决一个问题:上面的运算符重载函数是写在类外面的,日期类的成员变量是private私有的,该运算符重载函数还是不能用。

🌟注意:

  • 🌏不能通过连接其他符号来创建新的操作符:比如operator@
  • 🌏重载操作符必须有一个自定义类型参数
(int x1, int x2)都是内置类型是不可以的规定必须有一个自定义类型的参数
也就是说只能对自定义类型进行重载,内置类型不可以
bool operator<(int x1, int x2)
{
  if (x1._year < x2._year)
  {
    return true;
  }
  else if (x1._year == x2._year && x1._month < x2._month)
  {
    return true;
  }
  else if (x1._year == x2._year && x1._month == x2._month && x1._day < x2._day)
  {
    return true;
  }
  else
  {
    return false;
  }
}
  • 🌏用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义
  • 🌏作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
d1<d2两个操作数是不可以随意换位置的,左操作数就是第一个参数(this),右操作数就是第二个参数(d)
d1.operator<(d2)
bool operator==(Date* this, const Date& d2)
这里需要注意的是,左操作数是this,指向调用函数的对象(和上面一个意思)
bool 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 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;
  }
}
int main()
{
  Date d1(2023, 10, 1);
  Date d2(2022, 2, 15);
  cout << (d1 < d2) << endl;
  cout << (d1.operator<(d2)) << endl;
  成员函数---符合调用规则因为有一个是隐含的参数(d1.operator(d2))
  return 0;
}
——————————————————————————————————————————————————————————————————————————————————
类外面定义函数
bool operator<(const Date& x1, const Date& x2)
{
  if (x1._year < x2._year)
  {
    return true;
  }
  else if (x1._year == x2._year && x1._month < x2._month)
  {
    return true;
  }
  else if (x1._year == x2._year && x1._month == x2._month && x1._day < x2._day)
  {
    return true;
  }
  else
  {
    return false;
  }
}
int main()
{
  Date d1(2023, 10, 1);
  Date d2(2022, 2, 15);
  cout << (d1 < d2) << endl;
  cout << (operator<(d1, d2)) << endl;
  return 0;
}
  • 🌏不能改变操作符的操作数个数,一个操作符是几个操作数,那么重载的时候就有几个参数
  • 🌏(.* 和 *不一样 * 是可以重载的) 、 (域作用限定符 ::)、 sizeof 、(三目运算符?:) 、(对象变量取成员 .) 注意以上5个运算符不能重载。这个经常在笔试选择题中出现选择题。

三、赋值运算符重载

🌟 首先要区分赋值运算符和拷贝构造:

  • 赋值:两个已经存在的对象进行拷贝
  • 拷贝构造:一个已经存在的对象去初始化另一个对象
Date d1(2023, 10, 1);
Date d2(2023, 10, 7);
d1 = d2;     --->调用赋值运算符重载
Date d3 = d1;      --->调用拷贝构造函数;或者写成这种也是拷贝构造Date d3(d1);

1 .赋值运算符重载格式:

  • 参数类型:const T&,传递引用可以提高传参效率
  • 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
  • 检测是否自己给自己赋值
  • 返回*this :要复合连续赋值的含义
Date& operator=(const Data& d)
{
  if (this != &d)
  {
    _year = d._year;
    _month = d._month;
    _day = d._day;
  }
  return *this;
}

2 .赋值运算符只能重载成类的成员函数不能重载成全局函数:

我们可以重载赋值运算符。不论形参的类型是什么,赋值运算符都必须定义为成员函数。

class Date
{
public:
 Date(int year = 1900, int month = 1, int day = 1)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 int _year;
 int _month;
 int _day;
};
赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
Date& operator=(Date& left, const Date& right)
{
 if (&left != &right)
 {
 left._year = right._year;
 left._month = right._month;
 left._day = right._day;
 }
 return left;
}

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

3 .用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝:

用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。

注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

【C++从练气到飞升】05---运算符重载(二)+https://developer.aliyun.com/article/1502592


相关文章
|
1月前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
83 5
|
6月前
|
编译器 C++
C++进阶之路:何为运算符重载、赋值运算符重载与前后置++重载(类与对象_中篇)
C++进阶之路:何为运算符重载、赋值运算符重载与前后置++重载(类与对象_中篇)
53 1
|
3月前
|
C++
C++(十五) 运算符重载
C++中的运算符重载允许对已有运算符的功能进行重新定义,从而扩展语言功能、简化代码并提升效率。重载遵循特定语法,如 `friend 类名 operator 运算符(参数)`。重载时需注意不可新增或改变运算符数量、语义、优先级、结合性和返回类型。常见示例包括双目运算符 `+=` 和单目运算符 `-` 及 `++`。输入输出流运算符 `&lt;&lt;` 和 `&gt;&gt;` 也可重载。部分运算符只能作为成员函数重载。
|
6月前
|
存储 编译器 C++
【C++】:拷贝构造函数和赋值运算符重载
【C++】:拷贝构造函数和赋值运算符重载
32 1
|
6月前
|
C++ 索引
C++核心技术要点《运算符重载》
C++核心技术要点《运算符重载》
54 2
|
5月前
|
自然语言处理 程序员 C++
C++基础知识(五:运算符重载)
运算符重载是C++中的一项强大特性,它允许程序员为自定义类型(如类或结构体)重新定义标准运算符的行为,使得这些运算符能够适用于自定义类型的操作。这样做可以增强代码的可读性和表达力,使得代码更接近自然语言,同时保持了面向对象编程的封装性。
|
5月前
|
Java 程序员 C++
|
5月前
|
编译器 C++
【C++】详解运算符重载,赋值运算符重载,++运算符重载
【C++】详解运算符重载,赋值运算符重载,++运算符重载
|
6月前
|
编译器 C++
【C++】类和对象③(类的默认成员函数:赋值运算符重载)
在C++中,运算符重载允许为用户定义的类型扩展运算符功能,但不能创建新运算符如`operator@`。重载的运算符必须至少有一个类类型参数,且不能改变内置类型运算符的含义。`.*::sizeof?`不可重载。赋值运算符`=`通常作为成员函数重载,确保封装性,如`Date`类的`operator==`。赋值运算符应返回引用并检查自我赋值。当未显式重载时,编译器提供默认实现,但这可能不足以处理资源管理。拷贝构造和赋值运算符在对象复制中有不同用途,需根据类需求定制实现。正确实现它们对避免数据错误和内存问题至关重要。接下来将探讨更多操作符重载和默认成员函数。
|
6月前
|
C++
c++进阶篇(一)——运算符重载
c++进阶篇(一)——运算符重载