运算符重载
一、知识点总结
1、重载运算符的限制
(1)不能重载的运算符:. :: .* ?: sizeof
(2)可以重载的运算符:+ - * / % ^ & | ~ ! = < > += -= *= /= % ^= &= |= << >> >>= <<= == != <= >= && || ++ -- ->* ' -> [] () new delete new[] delete[]
(3)重载运算符函数可以对运算符做出新的解释,但原有基本语义不变:
不改变运算符的优先级
不改变运算符的结合性
不改变运算符所需要的操作数
不能创建新的运算符
2、运算符函数可以重载为成员函数或友元函数
(1)一元运算符
Object op 或 op Object
重载为成员函数解释为:Object . operator op ()
操作数由对象Object通过this指针隐含传递
重载为友元函数解释为:operator op (Object)
操作数由参数表的参数Object提供
(2)二元运算符
ObjectL op ObjectR
重载为成员函数解释为:ObjectL.operator op (ObjectR)
左操作数由ObjectL通过this指针传递,右操作数由参数ObjectR传递
重载为友元函数解释为:operator op (ObjectL,ObjectR)
左右操作数都由参数传递
(3)用成员函数重载运算符
成员运算符函数的原型在类的内部声明格式如下:
class X{
//...
返回类型operator 运算符(形参表);
//...
}
在类外定义成员运算符函数的 格式如下:
返回类型X::operator运算符(形参表)
{
函数体
}
(4)双目运算符重载为成员函数
对双目运算符而言,成员运算符函数的形参表中仅有一个参数,它作为运算符的右操作数,此时当前对象作为运算符的左操作数,它是通过this指针隐含地传递给函数的。
(5)一般而言,如果在类X中采用成员函数重载双目运算符@,成员运算符函数operator@所需的一个操作数由对象aa通过this指针隐含地传递,他的另一个操作数bb在参数表中显示,aa和bb是类X的两个对象,则以下两种函数的调用方法等价:
aa@bb;//隐式调用
aa.operator @ (bb);//显示调用
(6)单目运算符重载为成员函数
对单目运算符而言,成员运算符函数的参数表中没有参数,此时当前对象作为运算符的一个操作数。
一般而言,采用成员函数重载单目运算符时,以下两种方法等价:
@aa;//隐式调用
aa.operator@();//显示调用
成员运算符函数operator@所需的一个操作数由对象aa通过this 指针隐含地传递,因此,在他的参数表中没有参数。
(7)用友元函数重载
友元函数重载运算符常用于运算符左右操作数类型不同的情况。
在第一个参数需要隐式转换的情形下,使用友元函数重载运算符是正确的选择。
友元函数没有this指针,所需的操作数都必须在参数表显示声明,很容易实现类型的隐式转换。
C++中不能用友元函数重载的运算符有= () [] ->
成员运算符函数与友元运算符函数的比较
(1)成员运算符函数比友元运算符函数少带一个参数(后置的++ --需要增加一个形参)。
(2)双目运算符一般可以被重载为友元运算符函数或成员运算符函数,但当操作数类型不相同时,必须使用友元函数。
3
(1)运算符++和--有两种方式:
前置方式:++Aobject --Aobject
成员函数 重载 A::A operator++();
解释为:Aobject . operator++();
友元函数 重载 friend A operator ++(A&);
解释为:operator ++(Aobject);
后置方式:Aobject++ Aobject--
成员函数 重载 A::A operator++(int);
解释为:Aobject . operator++(0);//伪参数
友元函数 重载 friend A operator ++(A&,int);
解释为:operator ++(Aobject,0);
(2)重载赋值运算符
赋值运算符重载用于对象数据的复制
operator=必须重载为成员函数
重载函数原型为:类名&类名::operator=(类名);
//修改对象时调用重载复制运算符函数
(3)重载运算符[]和()
运算符[]和()是二元运算符
[]和()只能用成员函数重载,不能用友元函数重载
1)重载下标运算符[]
[]运算符用于访问数据对象的元素
重载格式 类型 类::operator[](类型);
例
设x是类X的一个对象,则表达式x[y]可被解释为x.operator[](y)
2)重载函数调用符()
()运算符用于函数调用
重载格式 类型 类::operator()(参数表);
例
设x是类X的一个对象,则表达式x(arg1,arg2,…)可被解释为x.operator()(arg1,arg2,...)
(3)重载流插入和流提取运算符
istream和ostream是C++的预定义流类
cin是istream的对象,cout是ostream的对象
运算符<<由ostream重载为插入操作,用于输出基本类型数据
运算符>>由istream重载为提取操作,用于输入基本数据
用友元函数重载<<和>>,输出和输入用户自定义的数据类型
重载输出运算符"<<"(只能被重载为友元函数,不能重载为成员函数)
定义输出运算符"<<"重载函数的一般格式如下:
ostream&operator<<(ostream&out,class_name&obj)
{out<<obj.item1;
out<<obj.item2;
...
out.obj.itemn;
return out;
}
重载输入运算符">>"(只能被重载为友元函数)
定义输入运算符"<<"重载函数的一般格式如下:
istream&operator<<(istream&in,class_name&obj)
{in>>obj.item1;
in>>obj.item2;
...
in>>obj.itemn;
return in;
}
二、感悟
1、重载运算符:作用是扩展应用范围,一般在类内进行。
2、成员函数重载:当前对象是第一个操作数,第一个操作数一定是对象本身,双目再加一个操作数。
3、友员函数重载比成员函数重载多一个参数。
4、友员:
(1)参数个数与操作数一致。
(2)不加类名限定。
5、自加自减:
成员函数:自加前置不需参数,本身对象是操作数;后置方式必须写参数(无用),第二个操作数是伪操作数。
6、a=n++;//把n赋值给a,n再加1
7、记录类:不能用浅复制,不是自己定义的是浅复制。
8、重载运算符主要应用于数据类,操作类基本不用。
9、不同之处:
(1)c1+c2:用c1的重载运算符函数,c2作为参数。
(2)c2+c1:用c2的重载运算符函数,c1作为参数。
10、对象调用成员函数完成重载。
11、友元函数重载:第一个操作数不再是对象本身。
12、返回的一定是自定义的对象。
13、(1)前加:值本身加一再返回;
(2)后加:值先参加其他运算再加一。
14、C++不允许将对象的值整体赋值给另一个对象,第一个操作数一定是对象本身。
15、protected:继承/保护的访问权限,比私有大比公有小。
16、重载赋值运算符:用一个已知对象的内容赋值给另一个对象,几个对象都已经存在时修改对象调用重载赋值运算符。
17、复制构造函数在创建对象时起作用,之后就不再起作用。
18、重载[]:确定在数组中的位置,必须使用成员函数重载,不能使用友元函数重载。
19、重载():用类的方式写了一个函数,是函数的另一种定义形式。
20、第一个操作数一定是流对象。
21、>>:提取运算符
<<:插入运算符
22、重载输入输出运算符:将数据对象整体输入输出。
23、
(1)复制构造函数:用一个已知对象创建另一个对象
(2)重载赋值运算符:用一个已知对象改变另一个对象
24、静态数据:属于所有对象共同拥有
25、只有静态函数可以操作静态数据
26、在一个类中重载<和>的内容不能相同,否则矛盾
27、用友元函数重载输入输出流,因为第一个成员不是成员对象
28、string 中的 find 是发现一个子串
29、当用两种或两种以上方式比较时,可以建立比较函数。
三、经典样例
enum style_enum{zero, one, two, three }; class CFraction {
private: int nume; // 分子
int deno; // 分母
public:
CFraction(int nu=0,int de=1):nume(nu),deno(de){} //构造函数,初始化用
void simplify(); //化简(使分子分母没有公因子)
friend ostream& operator<<(ostream &out,const CFraction &cf);
friend istream& operator>>(istream &in,CFraction &cf);
friend CFraction operator+(const CFraction &lcf,const CFraction &rcf);
friend bool operator<(const CFraction &lcf,const CFraction &rcf);
friend bool operator>=(const CFraction &lcf,const CFraction &rcf);
friend bool operator<=(const CFraction &lcf,const CFraction &rcf);
friend bool operator==(const CFraction &lcf,const CFraction &rcf);
friend bool operator!=(const CFraction &lcf,const CFraction &rcf);
CFraction operator+();
CFraction operator-(); };
void CFraction::simplify()
{ int v1 = nume;
int v2 = deno;
while(v2) {
int temp = v2;
v2 = v1 % v2;
v1 = temp; }
nume /= v1;
deno /= v1;
if(deno < 0)
{ deno = -deno; nume = -nume; } }
ostream& operator<<(ostream &out,const CFraction &cf)
{ out << cf.nume << '/' << cf.deno; return out; }
while(1)
{ in >> cf.nume >> ch >> cf.deno; if(cf.deno == 0)
cerr << "分母为0请重新输入\n"; else if(ch != '/')
cerr << "格式错误(形如m/n)请重新输入\n";
else break; }
return in; }
//加法重载
CFraction operator+(const CFraction &lcf,const CFraction &rcf)
{ CFraction cf;
cf.nume = lcf.nume*rcf.deno + lcf.deno*rcf.nume;
cf.deno = lcf.deno*rcf.deno;
cf.simplify();
return cf; }
//减法重载
CFraction operator-(const CFraction &lcf,const CFraction &rcf)
{ CFraction cf;
cf.nume = lcf.nume*rcf.deno - rcf.nume*lcf.deno;
cf.deno = lcf.deno*rcf.deno;
cf.simplify();
return cf; }
//乘法重载
CFraction operator*(const CFraction &lcf,const CFraction &rcf)
{ CFraction cf;
cf.nume = lcf.nume*rcf.nume;
cf.deno = lcf.deno*rcf.deno;
cf.simplify();
return cf; }
//除法重载
{ CFraction cf; cf.nume = lcf.nume*rcf.deno;
cf.deno = lcf.deno*rcf.nume;
cf.simplify();
return cf; }
//取正重载
CFraction CFraction::operator+()
{ simplify();
if(nume < 0)
nume = -nume;
return *this; }
//取负重载
CFraction CFraction::operator-()
{ simplify();
nume = -nume;
return *this; }
//大于号重载
bool operator>(const CFraction &lcf,const CFraction &rcf)
{ int l_nume = lcf.nume * rcf.deno;
int r_nume = rcf.nume * lcf.deno;
int common_deno = lcf.deno * rcf.deno; if((l_nume-r_nume) * common_deno > 0) return true;
return false; }
//小于号重载
bool operator<(const CFraction &lcf,const CFraction &rcf)
{ return !(lcf > rcf); }
//等于重载
{ return lcf.nume==rcf.nume && lcf.deno == rcf.deno; }
//不等于重载
bool operator!=(const CFraction &lcf,const CFraction &rcf)
{ return !(lcf==rcf); }
// bool operator>=(const CFraction &lcf,const CFraction &rcf)
{ if(lcf < rcf) return false;
return true; }
// bool operator<=(const CFraction &lcf,const CFraction &rcf)
{ if(lcf > rcf)
return false;
return true; }
CFraction cf1;
CFraction cf2;
cin >> cf1 >> cf2;
cout << "cf1: "<< cf1 << '\t' << "cf2: "<< cf2 << endl << "cf1 + cf2 : " << cf1+cf2 << endl << "cf1 - cf2 : " << cf1-cf2 << endl << "cf1 * cf2 : " << cf1*cf2 << endl << "cf1 / cf2 : " << cf1/cf2 << endl << " +cf1 : " << +cf1 << endl << " -cf1 : " << -cf1 << endl << " +cf2 : " << +cf2 << endl << " -cf2 : " << -cf2 << endl << " cf1 > cf2? 1/YES 0/NO " << (cf1 > cf2) << endl << " cf1 < cf2? 1/YES 0/NO " << (cf1 < cf2) << endl << " cf1 == cf2? 1/YES 0/NO " << (cf1 == cf2) << endl << " cf1 != cf2? 1/YES 0/NO " << (cf1 != cf2) << endl << " cf1 >= cf2? 1/YES 0/NO " << (cf1 >= cf2) << endl << " cf1 <= cf2? 1/YES return 0; }