运算符重载的概念
在C++中,所有系统预定义的运算符都是通过运算符函数来实现的。
a+b
operator+(a,b)
operator+(int,int)
重载为成员函数:在类中定义一个同名的运算符函数来重载该函数。
TYPE X::operator@ (形参表) {
//函数体
//重新定义运算符@在指定类X中的功能
}
如果重载单目运算符,就不必另设置参数;
如果是重载双目运算符,就只要设置一个参数作为右侧运算量,而左侧运算量就是该对象本身。
【例】定义一个表示复数的类Complex,并在该类中对运算符“+”进行重载,以实现两个复数的加运算。
#include <iostream>
using namespace std;
class Complex {
public:
Complex(double r=0.0,double i=0.0) {
real = r, image = i;
}
Complex operator+ (Complex &c1){
return Complex(real+c1.real,image+c1.image);
}
void show(){
cout<<"("<<real<<"+"<<image<<"i)"<<endl;
}
private:
double real, image;
};
int main() {
Complex c1(1, 2),c2(2, 3),c3;
c3 = c1+c2;
c1.show(), c2.show(), c3.show();
return 0;
}
重载为友元函数:定义一个与某一运算符函数同名的全局函数;
然后再将该全局函数声明为类的友元函数,从而实现运算符的重载。
friend TYPE operator@ (形参表);
说明:
(1)operator是关键字、@是需要被重载的运算符,TYPE是该运算符函数的返回值类型。
关键字operator与后面的运算符@共同组成了该运算符函数的函数名。
(2)对于双目运算符,参数表中包含两个参数:TYPE operator@(TYPE a, TYPE b)
(3)对于单目运算符,参数表中只包含一个参数:TYPE operator@(TYPE a)
【例】设计一个表示复数的类,在该类中对运算符 + - 以及 = 进行重载,以实现两个复数的加,减以及赋值运算。
#include<iostream>
using namespace std;
class Complex {
public:
Complex(double r=0.0,double i=0.0) {
real=r, image=i;
}
void print() {
cout<<"("<<real<<"+"<<image<<"i"<<")"<<endl;
}
Complex operator+(const Complex& c1) {
return Complex(real+c1.real,image+c1.image);
}
Complex operator-(const Complex& c2);
void operator=(const Complex& c3);
private:
double real, image;
};
Complex Complex::operator-(const Complex& c2) {
Complex c;
c.real = real-c2.real;
c.image = image-c2.image;
return c;
}
void Complex::operator=(const Complex &c3) {
real = c3.real;
image = c3.image;
}
int main() {
Complex a(3.0,4.0),b(1.0,2.0);
Complex c1,c2,c3;
c1 = a+b; c1.print();
c2 = a-b; c2.print();
c3 = c1; c3.print();
return 0;
}
运算符重载的方法
重载为成员函数
重载为友元函数
运算符重载的规则
(1)运算符是C++系统内部定义的,具有特定的语法规则,重载运算符时应注意该运算符的优先级和结合性保持不变。
(2)C++规定“.”、“*”、“::”、“?:”、sizeof 不能重载。同时也不能创造新的运算符。
(3)重载运算符时应注意使重载后的运算符的功能与原功能类似,原运算符操作数的个数应保持不变且至少有一个操作数是自定义类型数据。因此重载运算符函数的参数不能有默认值。
(4)由于友元函数破坏了类的封装性,所以重载单目运算符时一般采用成员函数的形式。
单目运算符可以作为类的成员函数重载,也可以作为类的友元函数重载,作为成员函数重载是没有参数,而作为友元函数重载时有一个参数。
(5)重载双目运算符时,若第一个操作数是类对象,则既可以采用成员函数形式也可以采用友元函数形式,若第一个操作数不是类对象,则只能采用友元函数形式。
当重载为类的成员函数时,运算符重载函数的形参个数要比运算符操作数个数少一个;
若重载为友元函数,则参数个数与操作数个数相同。
“=”、“()”、“[]”和“->”等运算符不能用友元函数方式重载。
常用运算符的重载
算术运算符的重载
【例】有一个Time类,包含数据成员minute(分)和sec(秒);
模拟秒表,每次走一秒,满60秒进一分钟,此时秒又从0开始算。要求输出分和秒的值。
C++约定: 在自增(自减)运算符重载函数中,增加一个int型形参,就是后置自增(自减)运算符函数。
#include <iostream>
using namespace std;
class Time {
public:
Time(int m=0,int s=0):minute(m),sec(s) {
}
//声明前置自增运算符 ++ 重载函数
Time operator++();
//声明后置自增运算符 ++ 重载函数
Time operator++(int);
void display( ) {
cout<<minute<<":"<<sec<<endl;
}
private:
int minute, sec;
};
Time Time::operator++() {
if(++sec >= 60) {
sec -= 60;
++minute;
}
return *this; //返回自加后的当前对象
}
Time Time::operator++(int) {
Time temp(*this);
sec++;
if(sec >= 60) {
sec -= 60;
++minute;
}
return temp; //返回的是自加前的对象
}
//注意前置自增运算符 ++ 和后置自增运算符 ++ 二者作用的区别。
//前者是先自加,返回的是修改后的对象本身。后者返回的是自加前的对象,然后对象自加。
int main( ) {
Time time1(34,59),time2;
cout<<" time1 : "; time1.display( );
++time1;
cout<<"++time1: "; time1.display( );
time2=time1++; //将自加前的对象的值赋给time2
cout<<"time1++: "; time1.display( );
cout<<" time2 :"; time2.display( ); //输出time2对象的值
}
关系运算符的重载
#include<iostream>
using namespace std;
class Time {
public:
Time(int h,int m,int s):hour(h),minute(m),second(s) {
}
int times() const{
return hour*60*60+minute*60+second;
}
bool operator<(const Time& t) {
return times() < t.times();
}
bool operator>(const Time& t) {
return times() > t.times();
}
private:
int hour,minute,second;
};
int main() {
Time t1(1,2,3), t2(1,2,4);
cout<<(t1<t2)<<endl;
cout<<(t1>t2)<<endl;
return 0;
}
逻辑运算符的重载
&& || !
//声明
friend int operator&& (const Array &ar1,const Array &ar2);
//重载函数
int operator&& (const Array &ar1,const Array &ar2){
int num=0;
for(int i=0; i<ar.n; i++){
for(int j=0; j<ar2.n; j++){
if(ar1.data[i]==ar2.data[j]){
num++; break;
}
}
}
return num;
}
//执行调用
cout<<(arr1 && arr2)<<endl;
位移运算符的重载
<< >>
//重载<<运算符
Array& operator<<(int num){
int temp;
for(int pass=1; pass<=num; pass++){
temp=data[0];
for(int i=0; i<n-1; i++){
data[i]=data[i+1];
}
data[n-1]=temp;
}
}
//执行调用
cout<<(arr<<3)<<endl;
下标访问运算符的重载
int& operator[] (int index){
if(index>=0 && index<=n-1) return data[index];
else {
cout<<"下标超出范围"<<endl;
exit(1);
}
}
赋值运算符的重载
默认的重载赋值运算符的功能是逐个拷贝一个对象的所有数据成员到另外一个对象。
当对象中包含动态分配内存空间的情况有可能出错,因为类的数据成员中包含指针,简单的赋值操作会使得两个对象中的指针成员指向同一个空间,运行时会发生错误,这时则需要用户自己定义重载的赋值运算符。
#include <iostream>
using namespace std;
class Complex {
public:
Complex(double r=0.0,double i=0.0):real(r),image(i) {
}
Complex& operator= (const Complex &c){
//运算符 + 重载为成员函数
real = c.real, imag = c.imag;
return *this;
}
void show(){
cout<<"("<<real<<"+"<<imag<<")"<<endl;
}
private:
double real, imag;
};
int main() {
Complex c1(1,2),c2;
c2 = c1;
c1.show(), c2.show();
return 0;
}
流输出与流输入运算符的重载
对 << 和 >> 重载的函数形式如下:
istream & operator >> (istream &,自定义类 &);
ostream & operator << (ostream &,自定义类 &);
//只能将重载 >> 和 << 的函数作为友元函数或普通的函数,而不能将它们定义为成员函数。
#include <iostream>
using namespace std;
class Complex {
public:
Complex(double r=0.0,double i=0.0):real(r),image(i) {
}
//运算符 << 重载为友元函数
friend ostream& operator<<(ostream& output, Complex & c5);
friend istream& operator>>(istream& input ,Complex& c4);
private:
double real, image;
};
ostream& operator<<(ostream& output, Complex & c5) {
output<<"("<<c5.real<<"+"<<c5.image<<"i)"<<endl;
return output;
}
istream& operator>>(istream& input ,Complex& c4) {
input>>c4.real>>c4.image;
return input;
}
int main() {
Complex c1,c2;
cin>>c1>>c2;
cout<<"c1="<<c1<<endl;
cout<<"c2="<<c2<<endl;
return 0;
}
复数的+ - =运算符的重载
重载流插入,流提取运算符实现复数的输入,输出。
#include <iostream>
using namespace std;
class Complex {
public:
Complex(double r=0.0,double i=0.0):real(r),image(i) {
}
Complex& operator+ (const Complex &c);//运算符 + 重载为成员函数
Complex& operator- (const Complex &c);
Complex& operator= (const Complex &c);
friend ostream& operator<<(ostream& output, Complex & c);//运算符 << 重载为友元函数
friend istream& operator>>(istream& input ,Complex& c);
private:
double real, imag;
};
Complex& Complex::operator+ (const Complex &c) {
real += c.real, imag += c.imag;
return *this;
}
Complex& Complex::operator- (const Complex &c) {
real -= c.real, imag -= c.imag;
return *this;
}
Complex& Complex::operator= (const Complex &c) {
real = c.real, imag = c.imag;
return *this;
}
ostream& operator<<(ostream& output, Complex & c) {
output<<"("<<c.real<<"+"<<c.imag<<"i)";
return output;
}
istream& operator>>(istream& input ,Complex& c) {
input>>c.real>>c.imag;
return input;
}
int main() {
Complex c1,c2,c3,c4,c5;
cin>>c1>>c2;
c3 = c1+c2;
c4 = c2-c1;
c5 = c1;
cout<<"c1="<<c1<<endl;
cout<<"c2="<<c2<<endl;
cout<<"c3="<<c3<<endl;
cout<<"c4="<<c4<<endl;
cout<<"c5="<<c5<<endl;
return 0;
}
不同类型数据之间的转换
方式一:定义运算符重载函数
//第一个对象是自定义数据类型,可以使用成员函数,友元函数
friend Complex operator+ (Complex c, double d);
Complex operator+ (Complex c, double d) {
return Complex(c.real+d, c.imag);
}
//第一个对象不是是自定义数据类型,只能使用友元函数
friend Complex operator+ (double d, Complex c);
Complex operator+ (double d, Complex c) {
return Complex(c.real+d, c.imag);
}
方式二:类型转化运算符重载函数
operator double(){
return real;
}
Complex c1(5, 6), c2(3, 4);
cout<<c1+2.3<<endl; // 执行 c1.operator double() 将 c1 转换为double
cout<<2.3+c2<<endl; // 执行 c2.operator double() 将 c2 转换为double
Complex(double r):real(r), imag(0){
} //转换构造函数
Complex c1(5, 6), c2(3, 4), c3;
c3 = c1+2.3; // 将 2.3 转换为 Complex 对象,并计算两个对象的值
字符串类
字符串String类
【例】创建一个字符串类String,并重载赋值运算符“=”为其成员函数。
#include <iostream>
#include <cstring>
using namespace std;
class String {
public:
String(char* p="") {
str = new char[strlen(p)+1];
strcpy(str,p);
}
~String() {
delete []str;
}
void print() {
cout<<str<<endl;
}
String& operator=(const String &s) {
delete []str;
str = new char[strlen(s.str)+1];
strcpy(str,s.str);
return *this;
}
private:
char* str;
};
int main() {
String s1("This is a string!"), s2;
s1.print();
s2.print();
s2 = s1;
s2.print();
return 0;
}
【例】创建一个字符串类String,并重载运算符 “!” 为其成员函数,用于判断对象中的字符串是否为空。
bool operator! (){
return strlen(str)!=0;
}
int main() {
String s1("This is a string!"), s2;
if(!s1) cout<<"s1 is not null"<<endl;
if(!s2) cout<<"s2 is not null"<<endl;
return 0;
}
【例】创建一个字符串类String,并重载运算符 “!” 为其友元函数,用于判断对象中的字符串是否为空。
friend bool operator! (const String &s);
bool operator! (const String &s) {
return strlen(s.str)!=0;
}
【例】设计一个字符串类String。并使其能完成以下功能:
(1)能使用“=”运算符完成两个字符串的赋值。
(2)能使用“== ” 运算符完成对两个字符串是否相等的判断。
(3)能使用“+=”运算符完成两个字符串的连接。编写相应的程序实现该类,并进行测试。
分析:
根据本题的题意,要设计的字符串类String,应包含1个指向字符串的指针p_str的数据成员和以下的成员函数:==
(1)相应的构造函数、拷贝构造函数和析构函数;==
(2)对=、==、+=三个运算符进行重载,使其能直接对两个字符串进行相应的运算;
(3)显示函数display, 用于显示对象中的字符串。
#include<iostream>
#include<cstring>
using namespace std;
class String {
public:
String(char* str=NULL);
String(String& s);
~String();
void operator=(String& str);
int operator==(String& str);
String operator+=(String& str);
void display() {
cout<<p_str<<endl;
}
private:
char* p_str;
};
String::String(char* str) {
if(str!=NULL) {
p_str=new char[strlen(str)+1];
strcpy(p_str,str);
} else p_str=NULL;
}
String::String(String& str) {
p_str=new char[strlen(str.p_str)+1];
strcpy(p_str,str.p_str);
}
String::~String() {
delete p_str;
}
void String::operator=(String& str) {
p_str=new char[strlen(str.p_str)+1];
strcpy(p_str,str.p_str);
}
int String::operator==(String& str) {
return (strcmp(p_str,str.p_str)==0);
}
String String::operator+=(String& str) {
char* s = new char[strlen(p_str)+1];
strcpy(s,p_str);
delete p_str;
p_str=new char[strlen(s)+strlen(str.p_str)+1];
strcpy(p_str,s);
strcat(p_str,str.p_str);
delete s;
return *this;
}
int main() {
String s1("ABC"),s2("abcde");
cout<<"字符串s1:"; s1.display();
cout<<"字符串s2:"; s2.display();
if(s1==s2) cout<<"字符串s1和字符串s2相等"<<endl<<endl;
else cout<<"字符串s1和字符串s2不相等"<<endl<<endl;
s2 = s1;
cout<<"赋值之后的字符串s2:"; s2.display();
if(s1==s2) cout<<"字符串s1和字符串s2相等"<<endl<<endl;
else cout<<"字符串s1和字符串s2不相等"<<endl<<endl;
s1 += s2;
cout<<"字符串s1和s2连接,连接之后的结果放在s1中,为:";
s1.display();
return 0;
}
程序实例
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
using namespace std;
const int N=1000;
class BigInt {
public:
BigInt() {
n=0;
for(int i=0; i<N; i++) num[i]=0;
}
BigInt(string s) {
int len=s.size();
for(int i=0; i<len; i++) num[i] = s[len-1-i]-'0';
n = len;
for(int i=n; i<N; i++) num[i]=0;
}
friend bool operator< (BigInt a,BigInt b);
friend BigInt operator+ (BigInt a, BigInt b);
friend BigInt operator- (BigInt a, BigInt b);
friend istream& operator>> (istream& input,BigInt& c);
friend ostream& operator<< (ostream& output,BigInt& c);
private:
int num[N], n;
};
bool operator< (BigInt a,BigInt b) {
if(a.n!=b.n) return a.n<b.n;
for(int i=a.n-1; i>=0; i--) {
if(a.num[i]!=b.num[i]) return a.num[i]<b.num[i];
}
return 0;
}
BigInt operator+ (BigInt a,BigInt b) {
BigInt c;
c.n = a.n > b.n ? a.n : b.n;
for(int i=0; i<c.n; i++) {
c.num[i] += a.num[i]+b.num[i];
c.num[i+1] += c.num[i]/10;
c.num[i] %= 10;
}
while(c.num[c.n]) c.n++;//进位
return c;
}
BigInt operator- (BigInt a,BigInt b) {
// a>=b
BigInt c;
c.n = a.n > b.n ? a.n : b.n;
for(int i=0; i<c.n; i++) {
c.num[i] = a.num[i]-b.num[i];
if(c.num[i]<0) {
a.num[i+1]--, c.num[i]+=10;
}
}
while(c.n>1 && c.num[c.n-1]==0) c.n--;
return c;
}
istream& operator>> (istream& input,BigInt& t) {
string s; cin>>s;
t.n = s.size();
for(int i=0; i<t.n; i++) t.num[i] = s[t.n-1-i]-'0';
return input;
}
ostream& operator<< (ostream& out,BigInt& t) {
for(int i=t.n-1; i>=0; i--) cout<<t.num[i];
return out;
}
int main() {
BigInt a,b; cin>>a>>b;
BigInt c = a+b;
cout<<a<<" + "<<b<<" = "<<c<<endl;
if(a<b) swap(a, b);
BigInt d = a-b;
cout<<a<<" - "<<b<<" = "<<d<<endl;
long long x = 987654321, y = 123456789;
cout<<x<<" - "<<y<<" = "<<x-y<<endl;
return 0;
}
大数四则运算
#include<iostream>
#include<string> // string
#include<cstring> // memset
#include<algorithm> // reverse
#include<iomanip> // setprecision
using namespace std;
const int N=1e6+10;
int A[N],B[N],C[N],la,lb,lc;
// 初始化,同时反转数组
void init(string a,string b) {
la = a.size(), lb = b.size();
memset(A, 0, sizeof(A));
memset(B, 0, sizeof(B));
memset(C, 0, sizeof(C));
for(int i=0; i<la; i++) A[i]=a[i]-'0';
for(int i=0; i<lb; i++) B[i]=b[i]-'0';
reverse(A, A+la), reverse(B, B+lb);
}
// 高精加高精
string add(string a,string b) {
init(a,b);
lc = max(la, lb);
for(int i=0; i<lc; i++) {
C[i] = A[i]+B[i]+C[i];
C[i+1] = C[i]/10;
C[i] %= 10;
}
while(C[lc]) lc++; //进位
string c;
for(int i=lc-1; i>=0; i--) c.append(1, C[i]+'0');
return c;
}
// 高精减高精
string sub(string a,string b) {
init(a, b);
lc = max(la, lb);
for(int i=0; i<lc; i++) {
C[i] = A[i]-B[i];
if(C[i]<0) A[i+1]--, C[i]+=10; // 借位
}
while(lc>1 && C[lc-1]==0) lc--;// 去除前导 0
string c;
for(int i=lc-1; i>=0; i--) c.append(1, C[i]+'0');
return c;
}
// 高精乘高精
string mul(string a,string b) {
init(a,b);
lc = la+lb-1;
for(int i=0; i<la; i++) {
for(int j=0; j<lb; j++) {
C[i+j] += A[i]*B[j];
}
}
for(int i=0; i<lc; i++) {
C[i+1] += C[i]/10;
C[i] %= 10;
}
while(C[lc]) lc++; // 进位
string c;
for(int i=lc-1; i>=0; i--) c.append(1, C[i]+'0');
return c;
}
// 高精除以低精
void div1(string a,int b) {
la = a.size(), lc = 0;
for(int i=0; i<la; i++) A[i]=a[i]-'0';
int x=0; // x 为被除数,最后为余数
for(int i=0; i<la; i++) {
x = x*10+A[i];
C[i] = x/b;
x %= b;
}
while(lc<la && C[lc]==0) lc++; // 去除前导 0
for(int i=lc; i<la; i++) cout<<C[i]; cout<<"..."<<x<<endl;
}
// return a>=b;
bool cmp(string a,string b) {
if(a.size() > b.size()) return 1;
if(a.size() < b.size()) return 0;
for(int i=0; i<a.size(); i++) {
if(a[i]>b[i]) return 1;
if(a[i]<b[i]) return 0;
}
return 1;
}
// 高精除以高精
void div2(string a,string b) {
string c, d;
for(int i=0; i<a.size(); i++) {
d = d.append(1, a[i]);// 余数可能为 0,需要去除前导 0
while(d.find('0')==0 && d.size()>1) d.erase(0, 1);
if(cmp(d, b)) {
for(int k=9; k>=1; k--) {
//试商
string x;
x.append(1, k+'0');
x = mul(x, b);
if(cmp(d, x)) {
d = sub(d, x);
c.append(1, k+'0');
break;
}
}
} else c.append(1, '0');// 不足商,则置 0
}
while(c.find('0')==0 && c.size()>1) c.erase(0,1);// 去除前导 0
cout<<c<<"..."<<d<<endl;
}
int main() {
// freopen("data.in", "r", stdin);
string a="987654321",b="123456789"; // cin>>a>>b;
long long x=987654321, y=123456789;
cout<<x<<" + "<<y<<" = "<<x+y<<endl;
cout<<x<<" + "<<y<<" = "<<add(a, b)<<endl;
cout<<x<<" - "<<y<<" = "<<x-y<<endl;
cout<<x<<" - "<<y<<" = "<<sub(a, b)<<endl;
cout<<x<<" * "<<y<<" = "<<1ll*x*y<<endl;
cout<<x<<" * "<<y<<" = "<<mul(a, b)<<endl;
cout<<x<<" / "<<y<<" = "<<x/y<<"..."<<x%y<<endl;
cout<<x<<" / "<<y<<" = "; div1(a, y);
cout<<x<<" / "<<y<<" = "; div2(a, b);
return 0;
}