c++学习之运算符重载详解

简介: c++学习之运算符重载详解

1.运算符重载的基本概念

运算符重载:就是对已有的运算符重新进行定义,赋予一种功能,以适应不同的数据类型。

运算符只能运算内置的数据类型,对于自定义的数据类型,不能运算,所以 我们可以重载运算符

语法:定义重载的运算符就像定义函数,只是函数的名字为operator+重载的符号。比如重载+,函数名就是operator+。

重载思路:

1.弄懂运算符的对象个数(个数决定重载函数时的参数)

2.识别运算符的运算对象 :是类的对象,还是其他

类的对象:全局函数实现(不推荐)         成员函数实现(推荐,少一个参数)可以用this指针

其他:只能是全局函数实现

比如我们在成员函数里重载+,对于ob1+ob2,在调用重载函数时:operator+(ob1,ob2)

对于重载函数的一般调用:会先ob1调用函数,为ob1.operator+(ob2),在ob2调用函数为ob1.operator+(ob2.operator+).

考虑到this指针是指向函数首地址,即ob1调用函数的地址。故我们在设计函数时,利用this指针,可以少写一个参数:

operator+(ob2),直接调用重载+的函数会替换为ob1.operator+(ob2).

2.重载加法运算符

这里我们先已简单的重载+运算符为例:

因为普通的加法只对默认的数据类型操作,我们自定义的类的对象是无法让操作符操作的。需要重载运算符实现某些运算。

比如:我们实现person类的相加是将person类里的age相加。

#define _CRT_SECURE_NO_WARNINGS
 using namespace std;
 class person
 {
 public:
   person(int age)
  {
  this-> age = age;
  }
   int age;
 };
 person operator+(person &p1, person &p2)
 {
  person p(p1.age+p2.age);
  return p;
 }
void test01()
{
   person p1(10);
   person p2(20);
   person p3 = p1 + p2;// operator+(p1,p2)
   cout << p3.age << endl;
 }
 int main()
 {
   test01();//结果为30
   return 0;
 }

这里我们定义了全局函数的+,重载了+运算符,但程序在对运算符调用时,还是会根据参数类型,调用重载后的运算符,还是调用默认的运算符。

同时我们说了运算符重载对于成员函数来说,我们是可以利用this指针的特性实现参数的优化

例如:

using namespace std;
 class person
 {
 public:
   person(int age)
 {
  this-> age = age;
 }
     person operator+(person & p2)
  {
     person p(this-> age + p2.age);
     return p;
  }
     int age;
  };
void test01()
{
   person p1(10);
   person p2(20);
   person p3 = p1 + p2;//    p1.operator+(p2)
   cout << p3.age << endl;
 }

我们直接在类里重载+运算符,我们直接p1的this指针调用重载的函数,实现参数的优化。

3.重载运算符<<(全局函数实现)

我们知道默认的cout<<只会输出基本的数据类型,如整形,字符型。想要访问类里的自定义对象,需要重载该运算符。举例:

#include<iostream>
#include<string>
using namespace std;
class person
{
private:
  //friend void operator<<(ostream& out, person& lucy);
    //friend ostream& operator<<(ostream& out, person& lucy);
  int num;
  string name;
  float score;
public:
  person(){}
  person(int num, string name, float score) :num(num), name(name), score(score){}
  void showperson(int num, string name, float score)
  {
    cout << num << endl;
    cout << name << endl;
    cout << score << endl;
  }
};
int main()
{
  person lucy(100, "lucy", 99.5);
  cout << lucy;
  return 0;
}

这里会直接报错,找不到该运算符的一个函数,该运算符无法对类的对象进行访问操作。

于是我们重载<<

void operator<<(ostream &out,person &lucy)//这里用到引用,还要友元
{
  out << lucy.num <<" " << lucy.name<<" " << lucy.score << endl;
  //返回该输出流变量
}

这里的cout的类型为ostream,我们参数直接定义运算符两边的类型的变量,这里直接利用引用,避免指针的麻烦使用。

因为这里访问的变量为私有的数据,如我们在类里还需要定义为友元函数。

我们可以从此理解所谓的运算符重载就是重新定一个函数,让操作符可以实现对数据的操作,而函数名我们用operator+运算符来表示。可以看到这里的函数仅仅是打印出数据,故在使用时,对于操作对象的<<是无法在与endl结合换行,因为该运算符的函数重载中只有打印出数据的功能。

那么如何让他拥有输出流类型的操作,这里我们可以改变重载函数类型:

ostream& operator<<(ostream &out,person &lucy)//这里用到引用,还要友元
{
  out << lucy.num <<" " << lucy.name<<" " << lucy.score << endl;
  //返回该输出流变量
    return out;
}

这里返回的新out是输出流类型,我们在使用时就可以

int main()
{
  person lucy(100, "lucy", 99.5);
  //这里想要输出数据num,是不可以的,私有数据
  //要想访问两种方法:1.利用成员函数  2.重载运算符
  //cout << lucy;  直接使用会报错,找不到该运算符的一个函数
  //程序运行对于<<进行查找若果类型左边是输出流,右边是person类,则会自动调用重载后的<<,反之,是默认的输出运算符
  cout << lucy;//重载后的<<是链式操作,此时这里无法在添加endl.
  //若想使用输出流类型的写法,函数类型为输出流,因为输出流可以和endl结合。
  cout << lucy<<endl;
  return 0;
}

还需要注意的一点关于函数类型后跟&:

在重载运算符时,为了避免不必要的对象拷贝,函数类型后面要跟&符号。这样可以将参数传递给函数时,传递的是对象的引用而不是对象的副本,从而避免了对象的拷贝操作,提高了程序的效率。此外,使用引用还可以避免修改对象的副本而不是实际对象的问题。因此,在重载运算符时,函数类型后面要跟&符号。

4.重载>>(输入)运算符(全局函数实现)

同输出运算符同理:

这里为了观察显眼,参数用的lucy,其实只要是传的类的对象就行。

class person
{
  friend ostream& operator<<(ostream& out, person& lucy);
  friend void operator>>(istream& in, person& lucy);
  friend istream& operator>>(istream& in, person lucy);
private:
  int num;
  string name;
  float score;
public:
  person() {}
  person(int num, string name, float score) :num(num), name(name), score(score){}
  void showperson(int num, string name, float score)
  {
    cout << num << endl;
    cout << name << endl;
    cout << score << endl;
  }
};
ostream& operator<<(ostream &out,person &lucy){
  out << lucy.num <<" " << lucy.name<<" " << lucy.score << endl;
}

重载输入运算符:

void operator>>(istream& in, person lucy)
{
  cin >> lucy.num >> lucy.name >> lucy.score;
}

主函数调用:

int main()
{
  person lucy;
  cin >> lucy;
  cout << lucy<<endl;
  return 0;
}

同样需要注意的是:因为这里的重载函数定义的类型是无返回类型,无法实现链接的功能,比如同时对多个对象输入

int main()
{
  person lucy;
    person bob;
  cin >> lucy >> bob;
  return 0;
}

还是改变函数类型

istream& operator>>(istream& in, person& lucy)
{
  cin >> lucy.num >> lucy.name >> lucy.score;
    return in;
}

8454a4afc3444a65bb01afb43fefcc98.png

5. 重载++/--运算符

重载++和--运算符时,有点让人不知所措,因为我们总希望能根据他们出现在对象的前面还是后面而来调用不同的重载函数,例如当看见++a,会调用前置加加重载函数,看见a++,会调用后置加加的重载函数。

++a ; 先自加 在使用

a++;//先使用 在自加

首先理解前置加加与后置加加的实质区别:

.前置加加返回的是引用

.后置加加返回的是对象

其次:

++无论前置还是后置,我们发现他的函数重载时设计的参数将会是一样,但这样就无法确定该调用哪一个,考虑到要让参数不一样而来调用对象的前置函数还是后置函数,于是多利用了一个占位参数

.前置加加调用TYPE& operator++()函数  比如++a,调用operator++(a)

.后置加加调用的是TYPE operator++(int)函数,也就是后置加加多了一个占位参数 .比如a++,调用

operator++(a,int)

.前置加加调用TYPE& operator--()函数  比如--a,调用operator--(a)

.后置加加调用的是TYPE operator--(int)函数,也就是后置加加多了一个占位参数 .比如a--,调用

operator--(a,int).

#define _CRT_SECURE_NO_WARNINGS 
#include <iostream>
 #include <string.h>
 #include <stdlib.h>
 #include <string>
 using namespace std;
 class Myint
 {
 public:
   Myint(int num)//this
     {
     this-> num = num;
     }
   //重载前置加加
   //先加加,再反回this
   Myint& operator++()
     {
     this-> num++;
     return *this;
     }
   //重载后置加加
   //先保存当前的this,在加加,返回当前的this
   Myint operator++(int)
     {
     Myint tmp = *this;
    this-> num++;
     return tmp;
        }
   int num;
   };
 //重载输出流<<
 ostream& operator<<(ostream & cout, Myint & p)
 {
   cout << p.num;
   return cout;
 }
 void test01()
 {
   Myint p1(10);
   cout << p1.num << endl;
   //operator++(p1)    p1.operator++()
   Myint p2 = p1++;
   cout << p2 << endl;
   Myint p3 = ++p1;
   cout << p3 << endl;
 }
 int main()
{
 test01();
 return 0;
}

7f499df506944409808f0f49eb40f94a.png

同理我们重载一下--

#define _CRT_SECURE_NO_WARNINGS 
#include <iostream>
 #include <string.h>
 #include <stdlib.h>
 #include <string>
 using namespace std;
 class Myint
 {
 public:
   Myint(int num)//this
     {
     this-> num = num;
     }
   //重载前置加加
   //先加加,再反回this
   Myint& operator--()
     {
     this-> num--;
     return *this;
     }
   //重载后置加加
   //先保存当前的this,在加加,返回当前的this
   Myint operator--(int)
     {
     Myint tmp = *this;
    this-> num--;
     return tmp;
        }
   int num;
   };
 //重载输出流<<
 ostream& operator<<(ostream & cout, Myint & p)
 {
   cout << p.num;
   return cout;
 }
 void test01()
 {
   Myint p1(10);
   cout << p1.num << endl;
   //operator--(p1)    p1.operator--()
   Myint p2 = p1--;
   cout << p2 << endl;
   Myint p3 = --p1;
   cout << p3 << endl;
 }
 int main()
{
 test01();
 return 0;
}

bf13ba284e894e40a0dabb3e735e3bba.png

6.重载==运算符

举例:判断person 对象lucy是否等于person对象bob,若相等打印相等,否则打印不相等

class person
{
  friend ostream& operator<<(ostream& out, person& lucy);
  friend istream& operator>>(istream& in, person& lucy);
  //friend bool operator==(person& oa, person& ob);
private:
  int num;
  string name;
  float score;
public:
  person() {}
  person(int num, string name, float score) :num(num), name(name), score(score){}
  void showperson(int num, string name, float score)
  {
    cout << num << endl;
    cout << name << endl;
    cout << score << endl;
  }   
  //成员函数重载operator==
  bool operator==(person &ob)
  {
    if (num == ob.num && name == ob.name && score == ob.score)
      return true;
    else
      return false;
  }
};
//全局函数重载operator==
//bool operator==(person &oa,person& ob)
//{
//  if (oa.num == ob.num && oa.name == ob.name && oa.score == ob.score)
//    return true;
//  else
//    return false;
//}
ostream& operator<<(ostream &out,person &lucy)//这里用到引用,还要友元
{
  out << lucy.num <<" " << lucy.name<<" " << lucy.score << endl;
  return out;
}
istream& operator>>(istream& in, person& lucy)
{
  in >> lucy.num >> lucy.name >> lucy.score;
  return in;
}
//判断lucy是否等于bob,若相等打印相等,否则打印不相等
int main()
{
  person lucy;
  person bob;
  cin >> lucy>>bob;
  if (lucy == bob)
  {
    cout << "相等" << endl;
  }
  else
  {
    cout << "不相等" << endl;
  }
  cout << lucy<<endl;
  return 0;
}

aa89d77b0506485d968245dcdf4f304d.png

7.可以重载的运算符

几乎C中所有的运算符都可以重载,但运算符重载的使用时相当受限制的。特别是不能使用C中当前没有意义的运算符(例如用**求幂)不能改变 运算符优先级,不能改变运算符的参数个数。这样的限制有意义,否 则,所有这些行为产生的运算符只会混淆而不是澄清寓语意。

710b2788fc384ef5811e5dc668f80fb5.png

9de68daa48294e9999ec160c2b4c1ba6.png

需要注意的是:

对于逻辑与与逻辑或,因为我们无法实现他的短路特性,一般不存在对它们的重载。

相关文章
|
4月前
|
算法 C语言 C++
C++语言学习指南:从新手到高手,一文带你领略系统编程的巅峰技艺!
【8月更文挑战第22天】C++由Bjarne Stroustrup于1985年创立,凭借卓越性能与灵活性,在系统编程、游戏开发等领域占据重要地位。它继承了C语言的高效性,并引入面向对象编程,使代码更模块化易管理。C++支持基本语法如变量声明与控制结构;通过`iostream`库实现输入输出;利用类与对象实现面向对象编程;提供模板增强代码复用性;具备异常处理机制确保程序健壮性;C++11引入现代化特性简化编程;标准模板库(STL)支持高效编程;多线程支持利用多核优势。虽然学习曲线陡峭,但掌握后可开启高性能编程大门。随着新标准如C++20的发展,C++持续演进,提供更多开发可能性。
83 0
|
2月前
|
编译器 C语言 C++
配置C++的学习环境
【10月更文挑战第18天】如果想要学习C++语言,那就需要配置必要的环境和相关的软件,才可以帮助自己更好的掌握语法知识。 一、本地环境设置 如果您想要设置 C++ 语言环境,您需要确保电脑上有以下两款可用的软件,文本编辑器和 C++ 编译器。 二、文本编辑器 通过编辑器创建的文件通常称为源文件,源文件包含程序源代码。 C++ 程序的源文件通常使用扩展名 .cpp、.cp 或 .c。 在开始编程之前,请确保您有一个文本编辑器,且有足够的经验来编写一个计算机程序,然后把它保存在一个文件中,编译并执行它。 Visual Studio Code:虽然它是一个通用的文本编辑器,但它有很多插
|
2月前
|
Java 编译器 C++
c++学习,和友元函数
本文讨论了C++中的友元函数、继承规则、运算符重载以及内存管理的重要性,并提到了指针在C++中的强大功能和使用时需要注意的问题。
23 1
|
3月前
|
C++
C++(十五) 运算符重载
C++中的运算符重载允许对已有运算符的功能进行重新定义,从而扩展语言功能、简化代码并提升效率。重载遵循特定语法,如 `friend 类名 operator 运算符(参数)`。重载时需注意不可新增或改变运算符数量、语义、优先级、结合性和返回类型。常见示例包括双目运算符 `+=` 和单目运算符 `-` 及 `++`。输入输出流运算符 `&lt;&lt;` 和 `&gt;&gt;` 也可重载。部分运算符只能作为成员函数重载。
|
5月前
|
存储 安全 编译器
【C++入门 四】学习C++内联函数 | auto关键字 | 基于范围的for循环(C++11) | 指针空值nullptr(C++11)
【C++入门 四】学习C++内联函数 | auto关键字 | 基于范围的for循环(C++11) | 指针空值nullptr(C++11)
|
5月前
|
人工智能 分布式计算 Java
【C++入门 一 】学习C++背景、开启C++奇妙之旅
【C++入门 一 】学习C++背景、开启C++奇妙之旅
|
5月前
|
存储 自然语言处理 编译器
【C++入门 三】学习C++缺省参数 | 函数重载 | 引用
【C++入门 三】学习C++缺省参数 | 函数重载 | 引用
|
5月前
|
小程序 C++
【C++入门 二 】学习使用C++命名空间及其展开
【C++入门 二 】学习使用C++命名空间及其展开
|
5月前
|
C++
C++基础知识(四:类的学习)
类指的就是对同一类对象,把所有的属性都封装起来,你也可以把类看成一个高级版的结构体。
|
5月前
|
自然语言处理 程序员 C++
C++基础知识(五:运算符重载)
运算符重载是C++中的一项强大特性,它允许程序员为自定义类型(如类或结构体)重新定义标准运算符的行为,使得这些运算符能够适用于自定义类型的操作。这样做可以增强代码的可读性和表达力,使得代码更接近自然语言,同时保持了面向对象编程的封装性。