C++程序设计教程
第 10讲,运算符重载 /5
§ 8.2 运算符重载
? 如何理解运算符的重载?
1。 运算符本身是一种函数。
如,c = a + b;
函数形式,
int add(int a,int b) { return (a+b); }
调用方式,c = add(a,b);
加法是一种运算
赋值也是一种运算
再如,
int a,b,c,z;
z = a + b + c;
如用函数表示应该是,
z = add(add(a,b),c);
如果 add(int a,int b)
没有返回值,则不能实现连
加!!
§ 8.2 运算符重载
2。 从函数角度来看,,重载, 是函数名称相同而入口参数数
据类型不同的。
因此,cout << a << b; 中的 << 运算符
和 int c = a << 2; 中的 << 的运算符
同样的运算符但调用对象不同,就可称为重载。
对加法而言,
? int ia = 1,ib = 2;
? int ic = ia + ib;
? float fa = 1.5f,fb = 2.5f;
? float fc = fa + fb;
? 这两者中的 + 如果看作函数的话,就是被重载了。
8.2.1 运算符重载的规则
1,C++允许重载现有的大多数运算符,而且只重
载已经有的运算符;
2,运算符的重载是通过编写函数来实现的,重载
之后的运算符的优先级和结合性都不会改变;
3,运算符重载是针对新的数据类型(类),对运
算符进行定义。除了 = 和 &,对象的运算符
必须重载,= 和 & 也可重载;
? [注意 ]
1,合理使用运算符的重载,使调用者容易理解;
2,赋值运算符 ( = ) 只是逐个拷贝类的数据成员,
这在很多情况下很危险。
运算符重载 例 8-1
class complex //复数类声明
{ public,
//构造函数
complex(double r=0.0,double i=0.0)
{ real=r; imag=i; }
void display() //显示复数的值
{ cout <<,(” << real <<,,” << imag
<<,)” << endl; }
private,
double real;
double imag;
};
例 8-1:加运算符 +
class complex //复数类声明
{ public,
//构造函数
complex(double r=0.0,double i=0.0)
{ real=r; imag=i; }
complex operator + (complex c2);
//+重载为成员函数
void display();
private,double real; double imag;
};
例 8-1:运算符 +
complex complex::operator + (complex c2)
//重载运算符 + 的实现
{ complex c;
c.real = c2.real + real;
c.imag = c2.imag + imag;
return complex(c.real,c.imag);
}
对运算符 +进行重载
int main() //主函数
{ complex c1(5,4),c2(2,10),c3;
c3 = c1 + c2; //使用重载运算符完成复数加法
cout << "c3=c1+c2=";
c3.display();
}
调用重载函数 +
可以被重载的运算符(双目)
?算术运算
? +(加),-(减),*(乘),/(除),%(取余)
?位运算
? <<(左移),>>(右移),|(或),&(与),^(异或)
?赋值运算
? =(赋值) +=,-=,/=,*=,|=,&=,^=
?关系和逻辑运算
? ==(相等),!=(不相等),>(大于),<(小于),
? >= (大于等于),<=(小于等于),
? ||(逻辑或),&&(逻辑与)
?分量运算 ->(数据成员),->*(指针型函数成员)
?其他运算,(关联语句) <<(输出流),>>(输入流)
可以被重载的运算符(单目)
?自增减及符号运算
? ++(自加),--(自减),+(正号),-(负
号)
?逻辑运算
? !(逻辑非),~(补)
?赋值运算
? &(取地址),*(按指针取值)
?其他运算
? () (类型转换),[](数组下标),new (创
建),delete (删除)
不能被重载的运算符
?分量运算
?,(取成员),.*(取指针型函数成员)
?作用域运算
?,:(单目全局),:,(双目局部)
?三目运算
??,(条件运算)
?取大小运算
? sizeof (取大小)
特别强调
?类的运算符实际上是函数的一种简捷表达式。
? 比如 int add(int a,int b); 可以用 a + b来表示。
?运算符的重载就是自己来定义这些运算符。
?每个类缺省的运算符有,(除不能被重载运算符以外)
? &(取地址) 和 = (赋值)
?其余的运算符必须重载以后才能使用。
?运算符重载时,其运算的实际结果应与运算符
原来的含义一致或相近。
§ 运算符重载的限制
1) 不能改变运算符的优先级;
2) 不能改变运算符的结合性;
3) 不能改变运算符操作数的个数;
4) 不能建立新的运算符;
5) 运算符函数的参数至少有一个必须是类的对
象或是对类的对象的引用;
6) 在重载运算符 (),[],->或者 =时,运算符重
载函数必须是类的成员;其它可以是友元;
7) 不能改变内部类型的运算。
8) 运算符 +被重载,并不等于 +=也自动被重载 。
运算符优先级
1 () [] ->,,,++(后缀 ) --(后缀 )
2 ! ~ ++ (前缀 ) -- (前缀 ) -(正 ) +(负 )
*(取值 ) & (取地址) (type) sizeof
3 ->*,*
4 * (乘 ) / (除 ) % (余 )
5 + (加 ) - (减 )
6 << (左移 ) >> (右移 )
7 <(小于 ) <= (小于等于 ) > (大于 ) >= (大于等于 )
8 == !=
9 &(位与 ) ^ (位异或 ) | (位或 )
运算符优先级
10 && (逻辑与 ) || (逻辑或 )
11?,(条件运算 )
12 = += -= *= /= %= &= ^= |=
<<= >>=
13,(逗号 )
例 8-2:单目运算符 ++
class Clock //时钟类声明
{
public,//外部接口
Clock(int NewH=0,int NewM=0,int NewS=0);
void ShowTime();
Clock& operator ++(); //前置单目运算符重载
Clock operator ++(int); //后置单目运算符重载
private://私有数据成员
int Hour,Minute,Second;
};
//前置单目运算符重载函数
Clock& Clock::operator ++()
{ Second++;
if( Second >= 60 )
{
Second = Second-60; // 60秒进 1分
Minute++;
if(Minute>=60)
{
Minute = Minute-60; // 60分进 1小时
Hour++;
Hour = Hour%24; // 24小时循环
}
}
return *this;
}
//后置单目运算符重载函数
Clock Clock::operator ++(int)
{ //注意形参表中的整型参数
Clock old=*this;
++(*this);
return old;
} int main()
{ Clock myClock(23,59,59);
cout<<"First time output:";
myClock.ShowTime();
cout<<"Show myClock++:";
(myClock++).ShowTime();
cout<<"Show ++myClock:";
(++myClock).ShowTime();
}
First time output,23:59:59
Show myClock++,23:59:59
Show ++myClock,0:0:1
用作类成员函数或友元函数的
运算符函数
? 运算符函数可以是成员函数,也可以是非成员函数。
? 非成员函数通常是友元函数。
? 当运算符函数是一个成员函数时,最左边的操作数必须
是运算符类的一个类对象。
? 如,CTime a,b,c; c = a + b;
? 运算 a + b, 对于 +, a 是左操作数
? 运算 c = …, 对于 =, c 是左操作数
? 如果左边操作数必须是另一个类的对象,或者是一个其
他类型的对象,运算符函数必须是非成员函数,只能用
友元( friend)函数。
? 如,cout << phone;
? cin >> phone; // << 和 >> 运算符应属于 phone
? 设计运算符函数时尽量用成员函数,避免用友元函数。
友元函数和友元类 friend
?一个类的友元函数是在该类作用域之外定义的:
友元不是该类的成员。
?一个类的友元可以访问该类的所有成员,包括私
有成员和受保护成员。
?友元必须在类中事先声明。
?友元关系是不对称且不传递的。
class Man //友元的宿主类
{ friend class Thief; //声明友元类
public,
Man(),money(100){}; //将初始的金钱数设置为 100
void SpendMoney() { money --; }
int ReportMoney() { return money; }
private,
int money;
};
class Thief //友元类的定义
{
public,
void ThieveMoney(Man& haplessMan)
{ haplessMan.money -= 10; }
};
int main()
{ Man man;
man.SpendMoney();
cout << man.ReportMoney() << endl; // 99
Thief thief;
thief.ThieveMoney(man);
cout << man.ReportMoney() << endl; // 89
return 0;
}
99
89
例 8-3,friend + 和 -
class complex //复数类声明
{
public,//外部接口
complex(double r=0.0,double i=0.0)
{ real = r; imag = i; } //构造函数
friend complex operator +
(complex c1,complex c2);//运算符 +重载为友元函数
friend complex operator –
(complex c1,complex c2); //运算符 -重载为友元函数
void display(); //显示复数的值
private,//私有数据成员
double real;
double imag;
};
complex operator +(complex c1,complex c2)
//运算符重载友元函数实现
{ return
complex(c2.real+c1.real,c2.imag+c1.imag);
}
complex operator -(complex c1,complex c2)
//运算符重载友元函数实现
{ return
complex(c1.real-c2.real,c1.imag-c2.imag);
}
// 其他函数和主函数同例 8-1
?
第 10讲,运算符重载 /5
§ 8.2 运算符重载
? 如何理解运算符的重载?
1。 运算符本身是一种函数。
如,c = a + b;
函数形式,
int add(int a,int b) { return (a+b); }
调用方式,c = add(a,b);
加法是一种运算
赋值也是一种运算
再如,
int a,b,c,z;
z = a + b + c;
如用函数表示应该是,
z = add(add(a,b),c);
如果 add(int a,int b)
没有返回值,则不能实现连
加!!
§ 8.2 运算符重载
2。 从函数角度来看,,重载, 是函数名称相同而入口参数数
据类型不同的。
因此,cout << a << b; 中的 << 运算符
和 int c = a << 2; 中的 << 的运算符
同样的运算符但调用对象不同,就可称为重载。
对加法而言,
? int ia = 1,ib = 2;
? int ic = ia + ib;
? float fa = 1.5f,fb = 2.5f;
? float fc = fa + fb;
? 这两者中的 + 如果看作函数的话,就是被重载了。
8.2.1 运算符重载的规则
1,C++允许重载现有的大多数运算符,而且只重
载已经有的运算符;
2,运算符的重载是通过编写函数来实现的,重载
之后的运算符的优先级和结合性都不会改变;
3,运算符重载是针对新的数据类型(类),对运
算符进行定义。除了 = 和 &,对象的运算符
必须重载,= 和 & 也可重载;
? [注意 ]
1,合理使用运算符的重载,使调用者容易理解;
2,赋值运算符 ( = ) 只是逐个拷贝类的数据成员,
这在很多情况下很危险。
运算符重载 例 8-1
class complex //复数类声明
{ public,
//构造函数
complex(double r=0.0,double i=0.0)
{ real=r; imag=i; }
void display() //显示复数的值
{ cout <<,(” << real <<,,” << imag
<<,)” << endl; }
private,
double real;
double imag;
};
例 8-1:加运算符 +
class complex //复数类声明
{ public,
//构造函数
complex(double r=0.0,double i=0.0)
{ real=r; imag=i; }
complex operator + (complex c2);
//+重载为成员函数
void display();
private,double real; double imag;
};
例 8-1:运算符 +
complex complex::operator + (complex c2)
//重载运算符 + 的实现
{ complex c;
c.real = c2.real + real;
c.imag = c2.imag + imag;
return complex(c.real,c.imag);
}
对运算符 +进行重载
int main() //主函数
{ complex c1(5,4),c2(2,10),c3;
c3 = c1 + c2; //使用重载运算符完成复数加法
cout << "c3=c1+c2=";
c3.display();
}
调用重载函数 +
可以被重载的运算符(双目)
?算术运算
? +(加),-(减),*(乘),/(除),%(取余)
?位运算
? <<(左移),>>(右移),|(或),&(与),^(异或)
?赋值运算
? =(赋值) +=,-=,/=,*=,|=,&=,^=
?关系和逻辑运算
? ==(相等),!=(不相等),>(大于),<(小于),
? >= (大于等于),<=(小于等于),
? ||(逻辑或),&&(逻辑与)
?分量运算 ->(数据成员),->*(指针型函数成员)
?其他运算,(关联语句) <<(输出流),>>(输入流)
可以被重载的运算符(单目)
?自增减及符号运算
? ++(自加),--(自减),+(正号),-(负
号)
?逻辑运算
? !(逻辑非),~(补)
?赋值运算
? &(取地址),*(按指针取值)
?其他运算
? () (类型转换),[](数组下标),new (创
建),delete (删除)
不能被重载的运算符
?分量运算
?,(取成员),.*(取指针型函数成员)
?作用域运算
?,:(单目全局),:,(双目局部)
?三目运算
??,(条件运算)
?取大小运算
? sizeof (取大小)
特别强调
?类的运算符实际上是函数的一种简捷表达式。
? 比如 int add(int a,int b); 可以用 a + b来表示。
?运算符的重载就是自己来定义这些运算符。
?每个类缺省的运算符有,(除不能被重载运算符以外)
? &(取地址) 和 = (赋值)
?其余的运算符必须重载以后才能使用。
?运算符重载时,其运算的实际结果应与运算符
原来的含义一致或相近。
§ 运算符重载的限制
1) 不能改变运算符的优先级;
2) 不能改变运算符的结合性;
3) 不能改变运算符操作数的个数;
4) 不能建立新的运算符;
5) 运算符函数的参数至少有一个必须是类的对
象或是对类的对象的引用;
6) 在重载运算符 (),[],->或者 =时,运算符重
载函数必须是类的成员;其它可以是友元;
7) 不能改变内部类型的运算。
8) 运算符 +被重载,并不等于 +=也自动被重载 。
运算符优先级
1 () [] ->,,,++(后缀 ) --(后缀 )
2 ! ~ ++ (前缀 ) -- (前缀 ) -(正 ) +(负 )
*(取值 ) & (取地址) (type) sizeof
3 ->*,*
4 * (乘 ) / (除 ) % (余 )
5 + (加 ) - (减 )
6 << (左移 ) >> (右移 )
7 <(小于 ) <= (小于等于 ) > (大于 ) >= (大于等于 )
8 == !=
9 &(位与 ) ^ (位异或 ) | (位或 )
运算符优先级
10 && (逻辑与 ) || (逻辑或 )
11?,(条件运算 )
12 = += -= *= /= %= &= ^= |=
<<= >>=
13,(逗号 )
例 8-2:单目运算符 ++
class Clock //时钟类声明
{
public,//外部接口
Clock(int NewH=0,int NewM=0,int NewS=0);
void ShowTime();
Clock& operator ++(); //前置单目运算符重载
Clock operator ++(int); //后置单目运算符重载
private://私有数据成员
int Hour,Minute,Second;
};
//前置单目运算符重载函数
Clock& Clock::operator ++()
{ Second++;
if( Second >= 60 )
{
Second = Second-60; // 60秒进 1分
Minute++;
if(Minute>=60)
{
Minute = Minute-60; // 60分进 1小时
Hour++;
Hour = Hour%24; // 24小时循环
}
}
return *this;
}
//后置单目运算符重载函数
Clock Clock::operator ++(int)
{ //注意形参表中的整型参数
Clock old=*this;
++(*this);
return old;
} int main()
{ Clock myClock(23,59,59);
cout<<"First time output:";
myClock.ShowTime();
cout<<"Show myClock++:";
(myClock++).ShowTime();
cout<<"Show ++myClock:";
(++myClock).ShowTime();
}
First time output,23:59:59
Show myClock++,23:59:59
Show ++myClock,0:0:1
用作类成员函数或友元函数的
运算符函数
? 运算符函数可以是成员函数,也可以是非成员函数。
? 非成员函数通常是友元函数。
? 当运算符函数是一个成员函数时,最左边的操作数必须
是运算符类的一个类对象。
? 如,CTime a,b,c; c = a + b;
? 运算 a + b, 对于 +, a 是左操作数
? 运算 c = …, 对于 =, c 是左操作数
? 如果左边操作数必须是另一个类的对象,或者是一个其
他类型的对象,运算符函数必须是非成员函数,只能用
友元( friend)函数。
? 如,cout << phone;
? cin >> phone; // << 和 >> 运算符应属于 phone
? 设计运算符函数时尽量用成员函数,避免用友元函数。
友元函数和友元类 friend
?一个类的友元函数是在该类作用域之外定义的:
友元不是该类的成员。
?一个类的友元可以访问该类的所有成员,包括私
有成员和受保护成员。
?友元必须在类中事先声明。
?友元关系是不对称且不传递的。
class Man //友元的宿主类
{ friend class Thief; //声明友元类
public,
Man(),money(100){}; //将初始的金钱数设置为 100
void SpendMoney() { money --; }
int ReportMoney() { return money; }
private,
int money;
};
class Thief //友元类的定义
{
public,
void ThieveMoney(Man& haplessMan)
{ haplessMan.money -= 10; }
};
int main()
{ Man man;
man.SpendMoney();
cout << man.ReportMoney() << endl; // 99
Thief thief;
thief.ThieveMoney(man);
cout << man.ReportMoney() << endl; // 89
return 0;
}
99
89
例 8-3,friend + 和 -
class complex //复数类声明
{
public,//外部接口
complex(double r=0.0,double i=0.0)
{ real = r; imag = i; } //构造函数
friend complex operator +
(complex c1,complex c2);//运算符 +重载为友元函数
friend complex operator –
(complex c1,complex c2); //运算符 -重载为友元函数
void display(); //显示复数的值
private,//私有数据成员
double real;
double imag;
};
complex operator +(complex c1,complex c2)
//运算符重载友元函数实现
{ return
complex(c2.real+c1.real,c2.imag+c1.imag);
}
complex operator -(complex c1,complex c2)
//运算符重载友元函数实现
{ return
complex(c1.real-c2.real,c1.imag-c2.imag);
}
// 其他函数和主函数同例 8-1
?