运算符重载
什么式运算符重载呢?在c++里对于两个整型变量a和b,有a+b,含义是将a+b加起来,对于对象呢,我们可以重载‘+’的含义,从而可以使对象a,b,相加,比如A类是描述复数的类,a,b是A类的两个对象,我们可以使用a+b来求出两个复数的相加结果。
简单来讲运算符重载就是 程序员可以自己 定义 自定义类型的数据结构的加减等操作。
不能重载的运算符:"." 、".*"、"::"、"?:"
重载只有运算符的优先级和结核性都不会改变。
双目运算符重载为成员函数
语法:
函数类型 operator 运算符(形参)
{
}
参数个数=原操操作数 -1(具体看下面例子)
规则:
如果要重载B为类成员函数,使之能够实现表达式oprd1 B oprd2其中oprd1为A类对象,则B应被重载为A类的成员对象,形参类型因应该是oprd2所属类型
经过重载后,表达式 oprd1 B oprd2 相当于oprd1.operator B(oprd2);
举个栗子:实现复数类加减法运算重载为成员函数
#include<iostream>
using namespace std;
class Complex
{
public:
Complex(double r=0.0,double i=0.0):real(r),imag(i){}
Complex operator + (const Complex &c2) const;
Complex operator - (const Complex &c2) const;
void display() const;
private:
double real;//实数部分
double imag;//虚数部分
};
Complex Complex::operator + (const Complex &c2) const
//创建一个临时无名对象作为返回值
{
return Complex(real+c2.real,imag+c2.imag);
}
Complex Complex::operator - (const Complex &c2) const
//创建一个临时无名对象作为返回值
{
return Complex(real-c2.real,imag-c2.imag);
}
void Complex::display()const
{
cout<<real<<","<<imag<<endl;
}
int main()
{
Complex c1(5,4),c2(2,10),c3;
cout<<"c1 = ";c1.display();
cout<<"c2 = ";c2.display();
c3=c1-c2;
cout<<"c3 = c1 - c2 = ";c3.display();
c3=c1+c2;
cout<<"c3 = c1 + c2 = ";c3.display();
return 0;
}
单目运算符重载
前置单目运算符 重载规则
如果用重载U为类成员函数,是指能够实现表达式 U oprd ,其中oprd为A类对象,则U应被承载为A类的成员函数,五形参。
经重载后,表达式U oprd相当于 oprd.operator U();
后置单目运算符 ++和--重载规则
如果要重载 ++或--为类的成员函数,实现oprd++ 或 oprd--,则++或--应被重载为A类的成员函数且应具有一个int类型的形参
这个int形参只是起到区分是 前置++后置++的作用。
前置++和后置++的例子
class A
{
public:
A(int x=0);
A& operator ++();//前置单目运算符重载
A operator ++(int);//后置单目运算符重载
private:
int x;
};
A & A::operator++()
{
x++;
return *this;
}
A A::operator++(int)
{
A old=*this;
++(*this);//调用前置++重载函数
return old;
}
如果是全局的函数,双目运算符需要两个参数,用来传运算符左右两个算子
Integer operator +(const Integer &c,const Integer &b)
{
return (c.n+b.n);
}
运算符重载——类型转换
C++会自动转换类型,比如下面的f函数,它的参数是Two类型的,但是主函数里给了它一个One类型的,然而Two类里有一个构造函数可以把One类型转换为Two类型,程序就会自动去调用这个构造函数去自动转换,如果不想这样的话,可以在那个构造函数前面加一个 explicit
class One
{
public;
One(){}
};
class Two
{
public:
Two(const One&){}
};
void f(Two)
{
}
int main()
{
One one;
f(one);
}
#include<iostream>
using namespace std;
class Integer
{
private:
int n;
public:
Integer(int x=0):n(x){}
Integer operator +(const Integer &c)
{
return (c.n+n);
}
};
int main()
{
Integer x,y,z;
z=x+y;
z=x+3;
z=3+y;
}
z=x+3是合法的,编译器在遇到加法后,会看加号前面那个变量是什么类型,再来确定是系统的加法还是重载的加法,这里由于加法是重载的加法,但是3是整型变量,程序会自动调用Integer类的构造函数,创造一个临时对象,来实现这个运算表达式.
如果将重载函数改一下,改成全局函数就可以实现z=3+y了
class Integer
{
private:
int n;
public:
Integer(int x=0):n(x){}
friend Integer operator +(const Integer &c,const Integer &d)
{
return (c.n+d.n);
}
};
重载函数模型
int是来区分前置还是后置,不会起作用
prefix++是前置++,postfix是后置++
运算符重载——赋值
#include<iostream>
using namespace std;
class A
{
public:
A(){}
};
class B
{
public:
B(int){}
B(const A&){}
};
int main()
{
B b = 1;
A a;
B c = a;//调用拷贝构造函数
c=a;//先把a变成一个B类的对象,然后把它赋值给c,调用了系统提供的默认赋值操作
return 0;
}
这个默认的赋值操作,在操作有指针成员变量的对象时候也会发生拷贝构造函数那样的问题,这时候我们要手动重载赋值运算符
模板
T& T::operator=(const T &rhs)
{
if(this !=&rhs)
{
//清空指针所指内存,分配内存指向被复制的那个对象。
}
return *this;
}
为什么要考虑是不是自己给自己赋值呢?
看下列代码
class A
{
char *p;
A& operator=(const A&that)
{
delete p;
p=new[strlen(that.p)+1];
strcpy(p,p.that.p);
return *this;
}
};
如果不考虑是不是自己给自己赋值,那么上述代码中delete p 这条代码把自己指针free了,下面的strlen和strcpy就出错了