一文搞懂C++中的运算符重载
作者:Mi?ronin 发布时间:2021-08-02 02:47:53
引入
对于基本类型的常量或变量进行运算时,我们可以使用 +、-、*、/ 等运算符,但是我们不可以使用运算符来进行对象之间的运算。
eg:对象之间的加法运算
class A
{
public:
A(int i=0,int j=0,int k=0):m_i(i),m_j(j),m_k(k){}
int geti() //接口
{
return m_i;
}
int getj()//接口
{
return m_j;
}
int getk()//接口
{
return m_k;
}
void Add(A &b)
{
int i=m_i+b.m_j;
int j=m_j+b.m_j;
int k=m_k+b.m_k;
cout<<i<<" "<<j<<" "<<k<<" "<<endl;
}
private:
int m_i;
int m_j;
int m_k;
};
void main()
{
A a(6,6,6);
A b(7,7,7);
a.Add(b);
}
正如例子中所说:这里我们无法进行对象之间的加法,对于加法操作我们需要使用接口,然后定义add函数进行加法的操作,这样也就导致了代码十分复杂。
为了使得为了当前程序的可读性更好,简单明了。所以我们引入运算符重载这个概念。
利用 C++ 提供的“运算符重载”机制,赋予运算符新的功能,就能解决用+将两个复数对象相加这样的问题。
一.运算符重载是什么
运算符重载:就是对已有的运算符赋予多重含义,使同一运算符作用于不同类型的数据时产生不同的行为。
运算符重载的目的是使得 C++ 中的运算符也能够用来操作对象。
运算符重载的实质是编写以运算符作为名称的函数。
二.运算符重载的格式
返回值类型 operator 运算符(形参表)
{
....
}
这里对于返回值类型 大家可能会产生疑惑:什么是返回值类型,这里对返回值类型进行说明。
返回类型主要包括三类:
值返回
引用返回
指针返回
值返回与引用返回 – 基本数据类型
值返回:由运算符操作的表达式只能作为右值
引用返回: 由运算符所操作的表达式可作为左值
指针返回:有指针的相关操作
void main()
{
int i =10;
int j =20;
int k =0;
i+j=k;// error 加号的表达式只可以做右值
//i+j有临时空间来存储表达式的值,但是没有确定的,不能把表达式的值放在i或者j空间
//不能作为左值的原因是 没有相应的内存空间 i有自己的空间 j有自己的空间 但是i+j没有
//右边要赋值给左边,那么左边必须要有确定的内存空间
k = i+j;//ok
++i = k;//i=30
/*
++i表达式的值 不管在什么时候都是i加过1之后的值,所以不用重新开辟临时空间存储表达式的值,用i的空间即可
++i=k;就是把k的值放在i的内存单元
*/
i++ = j;//error
/*
i++的j是i没有加过的值 但是最后i还是需要+1 所以此时i和i++不是同一个内存单元 所以需要重新开辟空间存储表达式的值,不可以这样使用
*/
(i=j)=40;//=运算符可以作为左值 即可以引用返回
}
这里告诉大家如何快速判断是值返回还是引用返回:
判断是左值还是右值
如果对左值右值区分不是很清楚,可以参考这篇博客:
链接: c++左值,右值,将忘值
三.部分运算符重载的实现
3.1 简单‘ + ’ ‘ - ’ ‘ * ’运算符重载
对于简单‘ + ’ ‘ - ’ ‘ * ’运算符重载举例如下:
#include<stdio.h>
#include<iostream>
using namespace std;
class A
{
public:
A(int i=0):m_i(i){}
A operator+(const A& t)//这里用const是为了本身值不改变
{
cout << "A(+)" << endl;
return m_i + t.m_i;
}
A operator-(const A& t)//这里用const是为了本身值不改变
{
cout << "A(-)" << endl;
return this->m_i-t.m_i;
}
A operator*(const A& t)//这里用const是为了本身值不改变
{
cout << "A(*)" << endl;
return this->m_i*t.m_i;
}
void print()
{
cout << m_i << endl;
}
private:
int m_i;
};
void main()
{
A a(10);
A b(20);
(a + b).print();
(a - b).print();
(a * b).print();
}
运算结果:
3.2 ++,- - 运算符
自增运算符++、自减运算符–都可以被重载,但是它们有前置、后置之分。
以++为例,假设 obj 是一个 CDemo 类的对象,++obj和obj++本应该是不一样的,前者的返回值应该是 obj 被修改后的值,而后者的返回值应该是 obj 被修改前的值。如果如下重载++运算符:
CDemo & CDemo::operator ++ ()
{
//...
return * this;
}
不论obj++还是++obj,都等价于obj.operator++()无法体现出差别。
为了解决这个问题,C++ 规定,在重载++或- -时,允许写一个增加了无用 int 类型形参的版本,编译器处理++或–前置的表达式时,调用参数个数正常的重载函数;处理后置表达式时,调用多出一个参数的重载函数。
对于前置运算符:左值 引用返回
对于后置运算符:右值 值返回
举例如下:
#include <iostream>
using namespace std;
class CDemo {
private:
int n;
public:
CDemo(int i=0):n(i) { }
CDemo & operator++(); //用于前置形式
CDemo operator++( int ); //用于后置形式
operator int ( ) { return n; }
friend CDemo & operator--(CDemo & );
friend CDemo operator--(CDemo & ,int);
};
CDemo & CDemo::operator++()
{//前置 ++
n ++;
return * this;
}
CDemo CDemo::operator++(int k )
{ //后置 ++
CDemo tmp(*this); //记录修改前的对象
n++;
return tmp; //返回修改前的对象
}
CDemo & operator--(CDemo & d)
{//前置--
d.n--;
return d;
}
CDemo operator--(CDemo & d,int)
{//后置--
CDemo tmp(d);
d.n --;
return tmp;
}
int main()
{
CDemo d(5);
cout << (d++ ) << ","; //等价于 d.operator++(0);
cout << d << ",";
cout << (++d) << ","; //等价于 d.operator++();
cout << d << endl;
cout << (d-- ) << ","; //等价于 operator-(d,0);
cout << d << ",";
cout << (--d) << ","; //等价于 operator-(d);
cout << d << endl;
return 0;
}
3.3 =运算符
同类对象之间可以通过赋值运算符=互相赋值。如果没有经过重载,=的作用就是把左边的对象的每个成员变量都变得和右边的对象相等,即执行逐个字节拷贝的工作,这种拷贝叫作“浅拷贝”。
对于深拷贝浅拷贝可以参考这篇博客:
链接: c++拷贝构造函数
有的时候,两个对象相等,从实际应用的含义上来讲,指的并不应该是两个对象的每个字节都相同,而是有其他解释,这时就需要对=进行重载。
如果没有写=重载 呢么类会提供默认的=重载
对于=运算符举例如下:
A& operator=(const A& b)
{
cout << "=" << endl;
m_i = b.m_i;
return *this;
}
3.4 <<,>>运算符
在 C++ 中,对于左移运算符<<可以和 cout 一起用于输出,因此也常被称为“输出运算符”。
实际上,对于<<来说,他本来并没有这样的功能,之所以能和 cout 一起使用,是因为被重载了。
cout 是 ostream 类的对象。ostream 类和 cout 都是在头文件 中声明的。ostream 类将<<重载为成员函数,而且重载了多次。为了使cout<<"Star War"能够成立,ostream 类需要将<<进行如下重载:
ostream & ostream::operator << (const char* s)
{
//输出s的代码
return * this;
}
cin 是 istream 类的对象,是在头文件 中声明的。istream 类将>>重载为成员函数,因此 cin 才能和>>连用以输入数据。一般也将>>称为“流提取运算符”或者“输入运算符”
istream & operator>>( istream & is,Complex & c)
{
return is;
}
四.运算符重载注意事项
1.可以重载成成员和友元两种形式
对象.operator运算符(第二个运算数)–成员形式
operator运算符(按照顺序写运算数)–友元形式
2.重载成成员形式,将第一个运算数省略(当成this)
重载成友元形式,参数不能省略 。
3.一般建议赋值重载为成员(如果第一个参数是本类对象,则重载魏本类的成员形式)
[]也建议重载为成员
<< >> 重载成友元,cout<<a.左操作数是cout的类对象,不可能是本类对象,只能重载为友元。
40运算符重载不可以改变优先级,结合性,操作数个数
五.运算符重载的限制
1.不可以臆造新的运算符
2.不可以改变原有运算符的优先级,语法等特点。
//不能将求模运算符(%)重载成使用一个操作数:
int x;
Time shiva;
% x;????//无效的求模运算符
% shiva;??//无效的重载操作符
3.运算符重载不可以使用太多
4.重载运算符含义必须清楚,不能有二异性质
5.以下运算符不可以重载
6.下列运算符只能通过成员函数进行重载
=:赋值运算符
():函数调用运算符
[]:下标运算符
->:通过指针访问类成员的运算符
六.MyString的简单实现
MyString.h
#ifndef MYSTRING_H
#define MYSTRING_H
#include<iostream>
using namespace std;
class mystring
{
char * _str;
public:
mystring(const char*str=nullptr);
mystring(const mystring &another);
mystring &operator=(const mystring&another);
mystring operator+(const mystring&another);
char& operator[](const int& i);
bool operator==(const mystring&another);
bool operator<(const mystring &another);
bool operator>(const mystring &another);
friend ostream &operator<<(ostream&out,mystring&str);
int size();
~mystring();
};
MyString.cpp
#include "mystring.h"
#include<string.h>
int mystring::size(){
return strlen(this->_str);
}
mystring::mystring (const char* str){
if(str==nullptr)
{
_str=new char[1];
_str[0]='\0';
return;
}
_str=new char[strlen(str)+1];
strcpy(_str,str);
}
mystring::mystring(const mystring &another){
int lenth=strlen(another._str);
_str=new char[lenth+1];
strcpy(_str,another._str);
}
mystring& mystring::operator=( const mystring&another){
if(*this==another)return *this;
else {
delete[] _str;
int lenth=strlen(another._str);
_str=new char[lenth+1];
strcpy(_str,another._str);
return *this;
}
}
mystring mystring:: operator+(const mystring&another){
mystring tmp;
delete[] tmp._str;
int lenth=strlen(_str)+strlen(another._str);
tmp._str=new char[lenth+1];
memset(tmp._str,0,lenth);
strcat(tmp._str,_str);
strcat(tmp._str,another._str);
return tmp;
}
char& mystring:: operator[](const int& i){
return _str[i];
}
bool mystring:: operator==(const mystring&another){
if( strcmp(_str,another._str)==0)
return true;
else return false;
}
bool mystring:: operator<(const mystring &another){
if( strcmp(_str,another._str)<0)
return true;
else return false;
}
bool mystring:: operator>(const mystring &another){
if( strcmp(_str,another._str)>0)
return true;
else return false;
}
ostream & operator<<(ostream&out,mystring&str){
for(int i=0;i<strlen(str._str);++i)
out<<str._str[i];
return out;
}
mystring:: ~mystring(){
delete[]_str;
}
测试函数:
#include<iostream>
#include<mystring.h>
#include<string>
using namespace std;
int main(){
mystring test1("hello");
cout<<test1<<endl;
cout<<test1[0]<<endl;
mystring test2("world");
cout<<test2<<endl;
mystring test3=test1+test2;
cout<<test3<<endl;
if(test1>test2)
cout<<test1<<" > "<<test2<<endl;
else if(test1==test2) cout<<test1<<" = "<<test2<<endl;
else cout<<test1<<" < "<<test2<<endl;
return 1;
}
结果:
来源:https://blog.csdn.net/weixin_56935264/article/details/127061754
猜你喜欢
- 有时候需要根据条件查询得出的数据较多,需要分页显示到页面上。这时点击下一页就不方便每次带查询条件在数据库中分页。可以在list中进行分页。p
- 一、做一个小测试通过注释,标注出下面两个类中每个方法的执行顺序,并写出studentId的最终值。package com.nezha.jav
- 一. 线程池简介1. 线程池的概念:线程池就是首先创建一些线程,它们的集合称为线程池。使用线程池可以很好地提高性能,线程池在系统启动时即创建
- 本文实例为大家分享了C# GDI+实现时钟表盘的具体代码,供大家参考,具体内容如下一、设计如下图界面按键“打开时钟&am
- 您已经看到很多包含视频内容的应用程序,比如带有视频教程的食谱应用程序、电影应用程序和体育相关的应用程序。您是否想知道如何将视频内容添加到您的
- 1、spring aop实现首先application-test.yml增加如下数据源的配置spring: datasource
- 每次写批量的时候,都要在网上搜索一下,虽然都做过多次了,但具体的自己还是记不住(汗颜),所以索性今天就记录下来。前期说明:foreach的主
- import java.util.concurrent.Semaphore;public class ThreeThread {
- 问题描述使用@Autowired处理多个同种类型的bean,出现@Value和@Bean的执行顺序问题。首先使用扫描包+注解的方式注册Use
- 1. 算法分析根据概率将奖品划分区间,每个区间代表一个奖品,然后抽取 随机数,反查落在那个区间上,即为所抽取的奖品。2. 代码核心
- Beanutils.copyProperties()用法及重写提高效率特别说明本文介绍的是Spring(import org.springf
- Warning:这是《Java 程序员进阶之路》专栏的第 55 篇。回来后小二找到了我,于是我就写下了这篇文章丢给他,并严厉地告诉他:再搞不
- [ThreadStatic] static char[]
- Activity设置全屏和无标题栏,要用到andorid.view.Window和Android.view.WindowManager。 W
- 题目描述这是 LeetCode 上的 768. 最多能完成排序的块 II ,难度为 困难。Tag : 「贪心」这个问题和&ldquo
- Spring Boot 2.7.6整合redis与低版本的区别最近在写程序的时候参考了之前写过的一篇文章spring boot整合redis
- namespace ConsoleTest{ class Program  
- 一、网络保存数据介绍可以使用网络来保存数据,在需要的时候从网络上获取数据,进而显示在App中。用网络保存数据的方法有很多种,对于不同的网络数
- Java内存区域与内存溢出异常概述对于 C 和 C++程序开发的开发人员来说,在内存管理领域,程序员对内存拥有绝对的使用权,但是也要主要到正
- 前言前面一篇我们介绍了使用 shared_preferences实现简单的键值对存储,然而我们还会面临更为复杂的本地存储。比如资讯类 App