公司动态
C++进阶教程:运算符重载与类型转换
本文是 C 系列教程的第 7 篇。上一篇深入了类与对象深拷贝、static、this、友元本篇讲解运算符重载与类型转换可重载运算符清单、常用重载、、、[]、()、、–、explicit 关键字、类型转换运算符。一、运算符重载基础1.1 什么是运算符重载运算符重载是让、、等运算符可以作用于自定义类型使代码更自然、更直观。例如c1 c2让复数相加cout obj直接打印对象。1.2 可重载与不可重载运算符可重载不可重载 - * / % ! [] () -- -规则不能发明新运算符至少一个操作数是自定义类型不能改变优先级与结合性。1.3 两种形式成员函数 vs 友元函数#includeiostreamusingnamespacestd;classPoint{private:intx,y;public:Point(inta,intb):x(a),y(b){}// 成员函数形式左操作数是 this只接受一个参数Pointoperator(constPointother)const{returnPoint(xother.x,yother.y);}// 友元函数形式需要两个参数能处理对称操作friendbooloperator(constPointa,constPointb);friendostreamoperator(ostreamos,constPointp);};booloperator(constPointa,constPointb){returna.xb.xa.yb.y;}ostreamoperator(ostreamos,constPointp){os(p.x, p.y);returnos;}intmain(){Pointp1(1,2),p2(3,4);Point p3p1p2;// 成员函数重载coutp3 p3endl;// (4, 6)cout(p1Point(1,2))endl;// 1cout(p1p2)endl;// 0return0;}二、常用运算符重载2.1 关系运算符 和 #includeiostreamusingnamespacestd;classStudent{private:string name;intscore;public:Student(string n,ints):name(n),score(s){}// 判断分数相同booloperator(constStudentother)const{returnscoreother.score;}// 按分数比较用于排序booloperator(constStudentother)const{returnscoreother.score;}// 可以用 实现booloperator(constStudentother)const{returnother*this;}intgetScore()const{returnscore;}};intmain(){Students1(张三,88),s2(李四,88),s3(王五,92);couts1 s2: (s1s2)endl;// 1couts1 s3: (s1s3)endl;// 1couts3 s1: (s3s1)endl;// 1return0;}2.2 输出运算符 #includeiostreamusingnamespacestd;classComplex{private:doublereal;doubleimag;public:Complex(doubler0,doublei0):real(r),imag(i){}// 重载 打印复数friendostreamoperator(ostreamos,constComplexc){osc.real;if(c.imag0)osc.imagi;elseosc.imagi;returnos;}};intmain(){Complexc1(3,4);Complexc2(2,-5);coutc1 c1endl;// 34icoutc2 c2endl;// 2-5ireturn0;}2.3 下标运算符 []#includeiostream#includestdexceptusingnamespacestd;classIntArray{private:intdata[10];intsize;public:IntArray():size(0){}voidadd(intv){if(size10)data[size]v;}// 下标运算符返回引用以便修改intoperator[](intindex){if(index0||indexsize){throwout_of_range(下标越界);}returndata[index];}// const 版本只读对象可用constintoperator[](intindex)const{if(index0||indexsize){throwout_of_range(下标越界);}returndata[index];}intgetSize()const{returnsize;}};intmain(){IntArray arr;arr.add(10);arr.add(20);arr.add(30);coutarr[0] arr[1] arr[2]endl;// 10 20 30arr[1]99;// 通过引用修改coutarr[1]endl;// 99return0;}2.4 自增自减 和 –#includeiostreamusingnamespacestd;classCounter{private:intvalue;public:Counter(intv0):value(v){}// 前置 返回引用支持链式Counteroperator(){value;return*this;}// 后置 参数 int 占位区分返回旧值副 Counteroperator(int){Counter temp*this;value;returntemp;}intget()const{returnvalue;}};intmain(){Counterc(5);coutc c.get()endl;// 5coutc (c).get()endl;// 6前置先增后取coutc (c).get()endl;// 6后置先取后增coutc c.get()endl;// 7return0;}2.5 函数调用运算符 ()#includeiostreamusingnamespacestd;// 仿函数像函数一样调用对象classAddBy{private:intbase;public:AddBy(intb):base(b){}// operator() 让对象可像函数调用intoperator()(intvalue)const{returnvaluebase;}};intmain(){AddByadd10(10);AddByadd100(100);coutadd10(5)endl;// 15对象当函数用coutadd100(5)endl;// 105return0;}三、赋值运算符与自赋值3.1 完整的赋值运算符#includeiostream#includecstringusingnamespacestd;classBuffer{private:char*data;intlength;public:Buffer(constchar*s){lengthstrlen(s);datanewchar[length1];strcpy(data,s);}Buffer(constBufferother){lengthother.length;datanewchar[length1];strcpy(data,other.data);}// 赋值运算符必须处理自赋值、资源释放、深拷贝Bufferoperator(constBufferother){if(thisother)return*this;// 自赋值检查关键delete[]data;// 释放旧资源lengthother.length;datanewchar[length1];strcpy(data,other.data);return*this;// 返回引用支持链式}~Buffer(){delete[]data;}constchar*get()const{returndata;}};intmain(){Bufferb1(Hello);Bufferb2(World);b1b2;// 赋值b1b1;// 自赋值安全coutb1.get()endl;// Worldreturn0;}3.2 default 与 deleteC11#includeiostreamusingnamespacestd;classNoCopy{public:NoCopy()default;// 使用编译器生成的默认构造// 禁用拷贝deleteNoCopy(constNoC opy)delete;NoCopyoperator(constNoCopy)delete;};classDummy{public:intvalue;Dummy()default;// 显式要求默认构造Dummy(intv):value(v){}};intmain(){NoCopy n1;// NoCopy n2 n1; // 错误拷贝已被删除Dummy d;d.value42;coutd.valueendl;// 42return0;}四、类型转换4.1 explicit 关键字防止隐式转换#includeiostreamusingnamespacestd;classMoney{private:doubleamount;public:// 非 explicitint 可隐式转 Money// Money(double a) : amount(a) {}// explicit禁止隐式转换必须显式构造explicitMoney(doublea):amount(a){}doubleget()const{returnamount;}};voidpay(constMoneym){cout支付 m.get() 元endl;}intmain(){Moneym1(100.0);// 显式构造// Money m2 200.0; // 错误explicit 禁止隐式转换// pay(300.0); // 错误不能隐式转 Moneypay(Money(300.0));// 显式转换后调用return0;}4.2 类型转换运算符#includeiostreamusingnamespXH‚\X[ۈœ]]N[[Nˆ[[‚XXX[ۊ[[ H[JK[ HB9ng/k9h/9)X[ۈOXB\]܈XJ Hۜˆ]\]X\XO[JH[ˆBX[ۈO;9b)9yd)..b!l;B^X]\]܈ Hۜˆ]\[HOHˆBY Hۜˆ][HȈ[H]X\XO \H[ˆBN‚[XZ[ HˆX[ۈ NˆXHH:f9o#/kXB]XH9/[B NHBY9hy.a9az:/k9h^X]9g*9i9g.kd9{BY Hˆ]b!kd:ggf툈[ˆB]\ŸB9.8 y9d9k/i#yl9n‚9d99aj:9z#9k9k9m99i#yl9n[YH[X[O\[[Y\XH‚\\^œ]]NXHX[ˆXH[XY‚XX\^XHHXHHH HX[K[XYJHB9/99$9df9ayl;B\^\]܊ۜ\^Hۜˆ turnComplex(realc.real,imagc.imag);}Complexoperator-(constComplexc)const{returnComplex(real-c.real,imag-c.imag);}Complexoperator*(constComplexc)const{// (abi)(cdi) (ac-bd) (adbc)ireturnComplex(real*c.real-imag*c.imag,real*c.imagimag*c.real);}// 比较友元friendbooloperator(constComplexa,constComplexb){returna.realb.reala.imagb.imag;}// 输出友元friendostreamoperator(ostreamos,constComplexc){osc.real;if(c.imag0)osc.imagi;elseosc.imagi;returnos;}// 输入友元friendistreamoperator(istreamis,Complexc){cout输入实部: ;isc.real;cout输入虚部: ;isc.imag;returnis;}};intmain(){Complexa(3,4);Complexb(1,-2);couta aendl;// 34icoutb bendl;// 1-2icouta b (ab)endl;// 42icouta - b (a-b)endl;// 26icouta * b (a*b)endl;// 11-2icouta b: (ab)endl;// 0return0;}总结本篇讲解了运算符重载的基础可重载清单、成员/友元两种形式、常用重载、、、W]、()、/–、赋值运算符的完整实现自赋值、深拷贝、default/delete、explicit 关键字和类型转换运算符并用复数类串联实战。重点掌握成员 vs 友元的选择、自赋值检查、前置/后置 的区别、explicit 防止意外转换。下一篇将讲解继承与多态虚函数、抽象类、RTTI敬请期待