C++中的 运算符重载

目录

1 运算符重载

1.1 概念

1.2 加号运算符重载

1.2.1 分类

1.2.1.1 成员函数重载 + 号

1.2.1.2 全局函数重载 + 号

1.3 左移运算符重载

1.3.1 示例

1.4 递增运算符重载

1.4.1 分类

1.4.1.1 前置递增

1.4.1.2 后置递增

1.4.2 总结

1.5 赋值运算符重载

1.5.1 示例

1.6 关系运算符重载

1.6.1 示例

1.7 函数调用运算符重载

1.7.1 示例

1.7.1.1 实现一个 打印 类

1.7.1.2 实现一个 加法 类


1 运算符重载

1.1 概念

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

1.2 加号运算符重载

+号一般代表2个数字之间相加,是内置数据类型, 而加号运算符重载代表2个自定义数据类型之间相加

1.2.1 分类

1.2.1.1 成员函数重载 + 号

首先创建代码,对象n1,n2,n3。尝试用n1+n2=n3;

 class number
 {
 public:
   int m_a;
   int m_b;
 };
 void test01()
 {
   number n1;
   n1.m_a = 10;
   n1.m_b = 10;
   number n2;
   n2.m_a = 5;
   n2.m_b = 6;
   number n3;
   n3 = n1 + n2;
 }

img

接下来,我们在类中创建加号运算符重载函数operator+

 class number
 {
 public:
   number operator+(number& n) // 加号运算符重载函数
   {
     number tmp;
     tmp.m_a = this->m_a + n.m_a;
     tmp.m_b = this->m_b + n.m_b;
     return tmp;
   }
   int m_a;
   int m_b;
 };

img

实际上number n3 = p1.operator+(p2);等价于 number n3 = n1 + n2;

成功相加

1.2.1.2 全局函数重载 + 号

同样的,只不过是在类外实现,而且参数是2个对象

 number operator+(number& n1, number& n2)
 {
   number tmp;
   tmp.m_a = n1.m_a + n2.m_a;
   tmp.m_b = n1.m_b + n2.m_b;
   return tmp;
 }

img

实际上number n3 = operator+(p1,p2);等价于 number n3 = n1 + n2;

  • 同时,运算符重载也可以发生函数重载

  • 而且,内置数据类型不可以修改

1.3 左移运算符重载

作用:以输出自定义数据类型

1.3.1 示例

首先创建一个person类与测试函数test01,类中有属性m_a/m_b

 class person
 {
 public:
   int m_a;
   int m_b;
 };
 ​
 void test01()
 {
   person p;
   p.m_a = 10;
   p.m_b = 5;
   cout << p.m_a << endl;
 }

如果只是想 输出m_am_b,可以直接输出

img

不过,如果想直接输出p,则不行

img

接下来,就可以使用operator函数重载左移运算符<<


不过首先,如果我们想要使用成员函数重载<<运算符,这么写

img

这样的话,实际调用效果是这样:

 p.operator(p) 

而如果参数改成cout

img

实际调用效果是这样:

 p.operator(cout) == p<<cout 
                //out在右侧

并非我们要的 cout<<p

因此,我们要使用全局函数重载

 void operator<<(ostream &cout, person &p)
 {
   cout << p.m_a << p.m_b;
 }

这样实际调用:

 operator << (cout,p) == cout << p;

img

成功输出

不过,如果我们想换行

img

因为<<返回的是void不可再输出其他内容。

因此,我们要使它仍然返回cout,修改operator函数

 ostream& operator<<(ostream& cout, person& p)
 {
   cout << p.m_a << p.m_b;
   return cout;
 }

img

这样就可以实现换行

甚至还可以随便输出其他内容

img


而此时,如果我们把属性设为私有,函数将无法访问

这样就可以使用上节课学到的友元来解决

 class person
 {
   friend ostream& operator<<(ostream& cout, person& p);
   friend void test01();
 private:
   int m_a;
   int m_b;
 };

1.4 递增运算符重载

对我们自定义数据类型进行递增,并非内置数据类型

1.4.1 分类

1.4.1.1 前置递增

首先创建myInteger类,并创建初始化函数myInteger

这样,在cout时就需要自定义<<左移运算符

 class myInteger
 {
 public:
 ​
   myInteger()
   {
     m_a = 5;
   }
   int m_a;
 };
 ostream& operator<<(ostream& cout, myInteger myint) // 自定义<<左移运算符重载
 {
   cout << myint.m_a << myint.m_b;
   return cout;
 }
 void test01()
 {
   myInteger myint;
   cout << ++myint << endl;
 }

img

此时提示没有对应的++运算符


接下来实现前置++

   myInteger& operator++()
   {
     m_a++; // 先自增
     return *this; // 再返回自身
   }

令 数据自增后返回即可,同时注意要返回自身返回值要引用&

img


如果返回值不加引用

   myInteger operator++()
   {
     m_a++; // 先自增
     return *this; // 再返回自身
   }

会导致每次返回的是新的对象,不是原来接收的数据本身

img

因此要加上&

1.4.1.2 后置递增

思路差不多,只是需要先保存值,再递增,最后输出保存的值

注意,只要在参数列表里写个int,编译器就会认为是后置递增了,而这个int占位参数

 myInteger operator++(int) // int 占位参数
   {
     myInteger tmp = *this;
     m_a++; // 先自增
     return tmp; // 再返回自身
   }

注意返回值不能写引用&,因为返回的是tmp,是局部变量函数执行完就被销毁了,引用会返回它自身但是已经被销毁了,相当于非法访问

img

因此,也不能连续递增(myint++)++

1.4.2 总结

  • 前置递增返回引用,后置递增返回值

  • 而递减与递增基本相同,只需有把++改成--即可


1.5 赋值运算符重载

前提:C++编译器至少给1个类添加4个函数

  • 默认构造函数(无参,空实现,函数体为空)

  • 默认析构函数(无参,空实现,函数体为空)

  • 默认拷贝构造函数(对属性进行值拷贝)

  • 赋值运算符operator=(对属性进行拷贝)

当值拷贝时,也会出现堆区内存重复释放的问题

1.5.1 示例

首先,创建person类和测试函数test01

 class person
 {
 public:
   person(int age)
   {
     m_age = new int(age); // 堆区开辟空间存储
   } 
   int *m_age;
 };
 ​
 void test01()
 {
   person p1(10);
   cout << *p1.m_age << endl;
   
   person p2(12);
   cout << *p2.m_age << endl;
 ​
   p2 = p1;
   cout << *p2.m_age << endl;
 }

img

正常输出

p2=p1是将p1所有属性浅拷贝给p2

而此时,由于我们上面new出的堆区数据需要我们手动释放,因此补全析构函数

 ~person()
   {
     if (m_age != NULL)
     {
       delete m_age;
       m_age = NULL;
     }
   }

再次运行

img

因为p1p2指向同一块m_agedelete一次后又进行delete,造成非法访问

这就是因为浅拷贝造成的堆区内存重复释放的问题

因此,我们赋值时,使用深拷贝,同时要先判断对象是否有属性在堆区,有则释放

   void operator=(person& p1) // 复制运算符的重载
   {
     if (m_age != NULL) // 判断是否有属性在堆区,有则释放
     {
       delete m_age;
       m_age = NULL;
     }
     m_age = new int(*p1.m_age);
   }

img

不会再崩溃

但是,如果想连续赋值

img

因为函数返回值是void,所以不能实现,因此修改返回值为person&,而且必须是引用才是返回自身,否则返回一个新的对象

 person& operator=(person& p1) // 复制运算符的重载,返回引用才是返回自身
   {
     if (m_age != NULL) // 判断是否有属性在堆区,有则释放
     {
       delete m_age;
       m_age = NULL;
     }
     m_age = new int(*p1.m_age);
     return *this;
   }

img成功实现

1.6 关系运算符重载

作用:重载关系运算符,使2个自定义对象进行对比

而内置数据类型如int等,编译器b本就知道如何比对

1.6.1 示例

 class person
 {
 public:
   person(string name, int age)
   {
     m_name = name;
     m_age = age;
   }
 private:
   string m_name;
   int m_age;
 };
 void test01()
 {
   person p1("joyce", 22);
   person p2("tatina", 22);
   if (p1 == p2)
   {
     cout << "相等" << endl;
   }
   else
   {
     cout << "不等" << endl;
   }
 }

img

目前是不行的

接下来实现对比函数

 bool operator==(person& p)
   {
     if (this->m_age == p.m_age && this->m_name == p.m_name)
     {
       return true;
     }
     return false;
   }

img

如果要实现不等关系!=判断,把==改为!=,判断条件反过来就行

1.7 函数调用运算符重载

函数调用运算符()也可以重载

1.7.1 示例

1.7.1.1 实现一个 打印 类
 class func
 {
 public:
 ​
   void operator()(string line) // 打印操作
   {
     cout << line << endl;
   }
   string line;
 };
 ​
 void test01()
 {
   func fun;
   fun("hello,nana");
 }

img

接下来实现一个同样作用的函数

 void print(string line)
 {
   cout << line << endl;
 }

img

正常输出,而由于使用非常类似于函数调用,因此又称为仿函数


1.7.1.2 实现一个 加法 类
 class my_add
 {
 public:
   int operator()(int x, int y)
   {
     return x + y;
   }
 };
 void test01()
 {
   my_add myadd;
   int ret = myadd(100, 200);
   cout << ret << endl;
 }

因此,仿函数非常灵活

同时,加上匿名函数对象

 void test01()
 {
   my_add myadd;
   int ret = myadd(100, 200);
   cout << ret << endl;
   // 匿名函数对象
   cout << my_add()(200, 300) << endl;
 }

img

  • 4
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值