运算符重载

基本概念

C++预定义的运算符,只能针对定义好的数据类型

不能用于对象

eg:

  • 在数学上,两个复数可以直接进行+、-等运算。

  • C++不允许

  • 对于两个复数对象complex_a和complex_b

    我们希望能直接写complex_a + complex_b

作用

  • 你可以对已有的运算符赋予多重的含义,使同一运算符作用于不同类型的数据时,导致不同的类型行为
  • 使提供的运算符能作用于对象
  • 运算符重载的实质是函数重载
  • 可以重载为普通函数,也可以重载为成员函数
  • 含运算符的表达式转换成对运算符函数的调用
  • 运算符被多次重载,则根据实参的类型决定调用哪个运算符函数

形式

返回值类型 operator 运算符(形参表)
{
    ……
}
#include<iostream>
#include<cstring>
using namespace std;
class Complex
{
    public:
        double real,imag;
        Complex(double r=0.0,double i=0.0):real(r),imag(i){ }
        Complex operator-(const Complex& c)
        {
            return Complex(real-c.real,imag-c.imag);
            //real表示当前对象的real值
        }
        //重载为成员函数时,参数个数为运算符目数-1
};
Complex operator+(const Complex& a,const Complex& b)
{
    return Complex(a.real+b.real,a.imag+b.imag);
    //返回一个临时对象
}
//重载为普通函数时,参数个数为运算符目数
int main()
{
    Complex a(4,4),b(1,1),c;
    c=a+b;
    cout<<c.real<<" "<<c.imag<<endl;
    c=a-b;
    cout<<c.real<<" "<<c.imag<<endl;
    cout<<(a+c).real<<" "<<(a+c).imag<<endl;
    cout<<(b-c).real<<" "<<(b-c).imag<<endl;
    //c=a+b => 等价于c=operator+(a,b)
    //c=a-b => 等价于c=a.operator-(b)
}

赋值运算符的重载

有时候希望赋值运算符两边的类型不匹配

比如把int类型变量赋值给一个Complex对象

!!!赋值运算符“=”只能重载为成员函数

#include<iostream>
#include<cstring>
using namespace std;
class String
{
    private:
        char* str;
    public:
        String ():str(new char[1]){str[0]=0;}
        const char* c_str() {return str;}
        //通过成员函数返回私有成员变量*str
        //这个成员函数是返回值的类型是const char*
        //但它不是常量成员函数,常量成员函数是后面加const
        String& operator=(const char* s)
        {
            delete[] str;
            str=new char[strlen(s)+1];
            strcpy(str,s);
            return *this;
        }//这是赋值
        ~String() {delete [] str;}//析构函数
};
int main()
{
    String s;
    s="Good Luck";
    //等价于 s.operator=("Good Luck")
    cout<<s.c_str()<<endl;
    //如果你写的是String s2="Hello!"
    //error!!! 因为这是个初始化语句,不是赋值语句,没有这样的构造函数
    s="Chen Huacan";
    cout<<s.c_str()<<endl;
}

浅拷贝和深拷贝

我们希望实现S1=S2,对象之间的直接赋值

  • 本来在系统原本的赋值含义下,此语句也不会报错
  • 但会使得s1是直接指向s2的那片空间
  • 坏处
    • s1的new出来的字符空间没有被delete[] 就直接废弃,形成内存垃圾
    • 双层析构消亡,可能程序会崩溃
    • 修改s1导致就会把s2的空间先delete[],导致s2无法使用
  • 因此需要自己重载运算符
String& operator=(const String& s)
        {
            delete[] str;
            str=new char[strlen(s.str)+1];
            strcpy(str,s.str);
            return *this;
        }
        //这两个赋值运算符的重载是两个版本,调用哪个看的是形参表

还有一个问题:当赋值语句为 s=s时,进去函数第一步就把s的str空间删除了。

String& operator=(const String& s)
        {
    		if(this==&s)
                return *this;
            delete[] str;
            str=new char[strlen(s.str)+1];
            strcpy(str,s.str);
            return *this;
        }
        //这两个赋值运算符的重载是两个版本,调用哪个看的是形参表

复制构造函数引发的问题

如果使用系统自动生成的复制构造函数

就会使用系统预定义的“=”,使得其面临上述s1=s2同样的困境

因此,需要自己写复制构造函数

String (String& s)
{
    str=new char[strlen(s.str)+1];
    strcpy(str,s.str);
}

运算符重载为友元函数

情景

  • 一般情况下,将运算符重载为类的成员函数,是较好的选择
  • 重载为成员函数不能满足使用要求
    • 例如:
      • Complex c;
      • c=c+5//编译能过,=> c=c.operator+(5);
      • 但是 c=5+c//error =>该情况,只有在全局函数时,参数表有两个时 要等价为 operator(c,5)时成立
  • 重载为普通函数,不能访问类的私有成员
  • 所以需要将运算符重载为友元
#include<iostream>
#include<cstring>
using namespace std;
class Complex
{
    double real,imag;//默认为private
    public:
        Complex(double r=0,double i=0):real(r),imag(i){ };
        Complex operator+(double r)
        {
            return Complex(real+r,imag);
        }//重载目的,使得复数能和浮点数相加
        int _Real(){return real;}
        int _Imag(){return imag;}
        friend Complex operator+ (double r,const Complex& c);
};
Complex operator+(double r,const Complex& c)
{
    return Complex(c.real+r,c.imag);
}
//注意这个参数位置也是应该维持r+C
//C+r就会默认调用成员函数的重载版本
int main()
{
    Complex a(2,1),b;
    b=5+a;
    cout<<b._Real()<<" "<<b._Imag()<<endl;
}

可变长数组类的实现

#include<iostream>
#include<cstring>
using namespace std;
class CArray
{
    int size;//数组元素个数,以方便实现length()成员函数
    int *ptr=NULL;//指向动态分配的数组
    public:
        //参数时最开始,定义时的空间大小,缺省值为0
        CArray(int s=0)
        {
            if(s==0)
                ptr=NULL;
            else
                ptr=new int[s];
        }

        CArray(CArray& a)
        {
            if(!a.ptr)
            {//如果a本来就是为空的,那么复制出来的对象直接为空并返回
                ptr=NULL;
                size=0;
                return;
            }
            //如果a不为空,则需要另外申请一片空间给他
            ptr=new int[a.size];
            memcpy(ptr,a.ptr,sizeof(int)*a.size);
            size=a.size;
        }//复制构造函数

        ~CArray()
        {
            if(ptr)
                delete[] ptr;
        }//析构函数

        void push_back(int v)
        {
            if(ptr)
            {
                int* tmpPtr=new int[size+1];
                memcpy(tmpPtr,ptr,sizeof(int)*size);
                delete[] ptr;
                ptr=tmpPtr;
            }
            else
                ptr=new int[1];
            ptr[size++]=v;
        }//用于在数组尾部添加一个元素

        CArray& operator= (const CArray& a)
        {
            if(ptr==a.ptr)
                return *this;
            //如果s=s,则直接返回
            if(a.ptr==NULL)
            {
                if(ptr)
                    delete[] ptr;
                ptr=NULL;
                size=0;
                return *this;
            }//赋值过来的东西是空的,要进行清空之后再过来
            if(size<a.size)
            {
                if(ptr)
                    delete[] ptr;
                ptr=new int[a.size];
            }
            memcpy(ptr,a.ptr,sizeof(int)*a.size);
            size=a.size;
            return *this;
        }//用于数组对象间的赋值

        int length()
        {
            return size;
        }//无参,返回数组元素个数

        int& operator[](int i)
        {
            return ptr[i];
        }
        //用于支持根据下标访问数组元素
        //需要既可以得到这个值,又能修改这个值
};

int main()
{
    CArray a;
    //这个类要有一个无参构造函数
    //使用动态分配内存来存储数组元素,因此需要一个指针成员变量
    for(int i=0;i<5;i++)
        a.push_back(i);
    //需要实现push_back()函数
    CArray a2,a3;
    a2=a;
    //需要重载“=”
    for(int i=0;i<a2.length();i++)
    {
        cout<<a2[i]<<" ";
        //需要重载“[]”使得其可以像下标一样输出东西
    }
    a2=a3;
    for(int i=0;i<a2.length();i++)
        cout<<a2[i]<<" ";
    cout<<endl;
    a[3]=100;
    CArray a4(a);
    //需要自己写复制构造函数
    for(int i=0;i<a4.length();i++)
        cout<<a4[i]<<" ";
    return 0;
}

流插入运算符和流提取运算符的重载

头文件定义了ostream类的对象

对“<<”(左移运算符)进行重载

cout<<5 => cout.operator<<(int n)
    
cout<<5<<"this" //如何实现这个
返回值应该为ostream& 类的引用:return *this
ostream& ostream::operator(int n)
    
等价于:cout.operator<<(5).operator<<("this")
//假定c是Complex复数类的对象,现在希望写cout<<c能直接输出“a+bi”形式

//同时我们还希望cin>>c能接受“a+bi”形式的输入
ostream& ostream::operator
#include<iostream>
#include<cstring>
#include<cstdlib>
using namespace std;
class Complex
{
    double real,imag;
    public:
        Complex(double r=0,double i=0):real(r),imag(i){ };
        //全局重载函数访问复数类的私有成员变量,因此需要声明友元
        friend ostream& operator<<(ostream& os,const Complex& c);
        friend istream& operator>>(istream& is,Complex& c);
};
ostream& operator<<(ostream& os,const Complex& c)
{
    os<<c.real<<"+"<<c.imag<<"i";
    //<<是ostream类里的成员函数,所以是os<<
    return os;
}
istream& operator>>(istream& is,Complex& c)
{
    string s;
    is>>s;
    //以字符串形式读入“a+bi”形式
    int pos=s.find("+",0);
    //找到实数部分
    string tmp=s.substr(0,pos);
    //s.substr(下标,字符长度)
    c.real=atof(tmp.c_str());
    //将字符串转换为数字
    tmp=s.substr(pos+1,s.size()-pos-2);
    c.imag=atof(tmp.c_str());
    return is;
}
int main()
{
    Complex c;
    cin>>c;
    cout<<c<<" "<<1<<endl;
}

类型转换运算符的重载

int m=6

double(m)

这个就称为类型转化运算符

#include<iostream>
using namespace std;
class Complex
{
    double real,imag;
    public:
        Complex(double r=0,double i=0):real(r),imag(i){ };
        operator double() {return real;}
        //重载强制类型转换运算符没有返回类型
        //事实上,默认的返回类型就是重载的这个运算符double()
};
int main()
{
    Complex c(1.2,3.4);
    cout<<(double)c<<endl;
    double n=2+c;
    //我们没有对加号进行重载,原则上一个整型不能和Complex类的复数进行加减
    //该式子等价于 double n=2+c.operator double()
    //编译器认为,如果能把c变成double类型就好了,于是调用重载运算符
    //!!!甚至int n也可以
    cout<<n;
    //输出3.2
}

自增、自减运算符的重载

自增运算符++和自减运算符–有前置和后置之分

为了区别:

  • 前置运算符作为一元运算符重载

    • T& operator++()
  • 后置运算符作为二元运算符重载

    这个多出来的参数是个没有用的参数

    • T operator++(int)
  • 在没有后置运算符重载,而有前置重载的情况下

    vs会调用前置

    devc++会error

#include<iostream>
using namespace std;
class CDemo
{
    private:
        int n;
    public:
        CDemo(int i=0):n(i){ }//构造函数
        CDemo& operator++()
        {
            n++;
            return *this;
        }//前置形式
        //因为C++传统里,++a的返回值本身为a的引用,优雅传承
        CDemo operator++(int )
        {
            CDemo tmp(*this);
            //记录修改前的对象
            ++n;
            return tmp;
            //返回修改前的对象
        } //后置形式
        operator int(){return n;}//使得这个类里的某些返回值可以为int
        friend CDemo& operator--(CDemo& );
        friend CDemo operator--(CDemo& ,int );
};

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)
    //这个d是一个类,如何输出
    //1.重载左移运算符
    //2.使d++这个成员函数返回值为可输出的数据类型
    //3.强制转换类型运算符重载
    cout<<d<<",";
    cout<<(++d)<<",";//=> d.operator++()
    cout<<d<<endl;
    cout<<(d--)<<",";//=> operator--(d,0)
    //定义为全局函数玩一玩
    cout<<d<<",";
    cout<<(--d)<<",";
    cout<<d<<endl;
    return 0; 
}

重载运算符的意义是:使得一整个类,不能够用该运算符的数据类型可以使用

  • C++不允许定义新的运算符
  • 重载后运算符的含义应该符合日常习惯:
    • complex_a+complex_b
    • word_a>word_b
    • date_b=date_a+n
  • 运算符重载不改变运算符的优先级
  • 以下运算符不能被重载: ”.“,”.*“,”::“,”?:“,sizeof
  • 重载运算符[] () -> 或者赋值运算符=时,运算符重载函数必须声明为类的成员函数
class CHugeInt {
	private:
		string str;
		int n=0;
	public:
		CHugeInt (string ch)
		{
			str=str+ch;
		}
		CHugeInt (int ch)
		{
			n=ch;
		}
		
		inline string operator+(CHugeInt& c1)
		{
			string tmp1="";
			string tmp2=to_string(c1.n);
			string tmp3="";
			tmp1=tmp1+str;
			if(tmp1.size()<tmp2.size())
				swap(tmp1,tmp2);
			int len1=tmp1.size();
			int len2=tmp2.size();
			for(int i=0;i<len1-len2;i++)
			{
				tmp2="0"+tmp2;
			}//给较短数加前缀0
			int up=0;
			for(int i=len1-1;i>=0;i--)
			{
				int t=tmp1[i]-'0'+tmp2[i]-'0'+up;
				up=t/10;
				tmp3[i]=t%10+'0';
			}
			string ans="";
			if(up>0)
				ans=ans+"1";
			for(int i=0;i<len1;i++)
			{
				ans=ans+tmp3[i];
			}
			return ans;
		}
		inline string operator+(int c1)
		{
			string tmp1="";
			string tmp2=to_string(c1);
			string tmp3="";
			tmp1=tmp1+str;
			if(tmp1.size()<tmp2.size())
				swap(tmp1,tmp2);
			int len1=tmp1.size();
			int len2=tmp2.size();
			for(int i=0;i<len1-len2;i++)
			{
				tmp2="0"+tmp2;
			}//给较短数加前缀0
			int up=0;
			for(int i=len1-1;i>=0;i--)
			{
				int t=tmp1[i]-'0'+tmp2[i]-'0'+up;
				up=t/10;
				tmp3[i]=t%10+'0';
			}
			string ans="";
			if(up>0)
				ans=ans+"1";
			for(int i=0;i<len1;i++)
			{
				ans=ans+tmp3[i];
			}
			return ans;
		}
		friend string operator+(int num,CHugeInt& c1)
		{
			string tmp1="";
			string tmp2=to_string(num);
			string tmp3="";
			tmp1=tmp1+c1.str;
			if(tmp1.size()<tmp2.size())
				swap(tmp1,tmp2);
			int len1=tmp1.size();
			int len2=tmp2.size();
			for(int i=0;i<len1-len2;i++)
			{
				tmp2="0"+tmp2;
			}//给较短数加前缀0
			int up=0;
			for(int i=len1-1;i>=0;i--)
			{
				int t=tmp1[i]-'0'+tmp2[i]-'0'+up;
				up=t/10;
				tmp3[i]=t%10+'0';
			}
			string ans="";
			if(up>0)
				ans=ans+"1";
			for(int i=0;i<len1;i++)
			{
				ans=ans+tmp3[i];
			}
			return ans;
		}
		CHugeInt& operator+=(int num)
		{
			n=n+num;
			return *this;
		}
		friend ostream& operator<<(ostream& os,const CHugeInt& c)
		{
			os<<c.n;
			return os;
		}
		CHugeInt& operator++()
		{
			++n;
			return *this;
		}
		CHugeInt operator++(int )
		{
			CHugeInt tmp(*this);
			++n;
			return tmp;
		}
};
高精度加法
    private:
		char num[220];
	public:
		//编写一个字符串逆序函数
		void reverse(char* tmp)
		{
			int len=strlen(tmp);
			int i=0,j=len-1;
			while(i<j)
			{
				swap(tmp[i],tmp[j]);
				++i;
				--j;
			}
			//双指针一直交换
		}
		CHugeInt(char* p)
		{
			//初始化num
			memset(num,0,sizeof(num));
			strcpy(num,p);
			reverse(num);
		}
		CHugeInt(int n)
		{
			memset(num,0,sizeof(num));
			sprintf(num,"%d",n);
			reverse(num);
		}
		CHugeInt operator+(int n)
		{
			return *this+CHugeInt(n);
		}//将类+数 转化成类+类
		CHugeInt operator+(const CHugeInt& n)const
		{
			CHugeInt tmp(0);
			int up=0;//进位
			for(int i=0;i<210;i++)
			{
				char c1=num[i];
				char c2=n.num[i];
				if(c1==0 && c2==0 && up==0)
					break;
				if(c1==0)
					c1='0';
				if(c2==0)
					c2='0';
				int k=c1-'0'+c2-'0'+up;
				if(k>=10)
				{
					up=1;
					tmp.num[i]=k-10+'0';
				}
				else
				{
					up=0;
					tmp.num[i]=k+'0';
				}
			}
			return tmp;
		}
		friend CHugeInt operator+(int n,const CHugeInt& h)
		{
			return h+n;
		}//转化为类+num,再转化为类+类
		friend ostream& operator<<(ostream& os,const CHugeInt& h)
		{
			int len=strlen(h.num);
			for(int i=len-1;i>=0;--i)
			{
				cout<<h.num[i];
			}
			return os;
		}
		CHugeInt& operator++()
		{
			*this=*this+1;
			return *this;
		}
		CHugeInt operator++(int)
		{
			CHugeInt tmp(*this);
			*this=*this+1;
			return tmp;
		}
		CHugeInt& operator+=(int n)
		{
			*this=*this+n;
			return *this;
		}

理解cin>>n

#include <iostream>
using namespace std;
class MyCin
{
	private:
		bool value;
	public:
		MyCin():value(true){ }
		operator bool()
		{
			return value;
		}
		//cin>>能判断是因为重载了bool,使得强制类型转换
		//我们也重载一个,当为false时就可以return false
		MyCin& operator>>(int& n)
		{
			cin>>n;
			if(n==-1)
				value=false;
			return *this;
		}	
};
int main()
{
    MyCin m;
    int n1,n2;
    while( m >> n1 >> n2) 
        cout  << n1 << " " << n2 << endl;
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Caaaaaan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值