基本概念
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;
}