文章目录
一、运算符重载
基本使用方法
运算符重载:又名“运算符多态”(函数重载==
函数多态)
使用方法:
operator [符号](参数列表)
// examples
operator +(const A &a){...}
// 当出现两个类相加时,代码替换为:
sum = A.operator+(B);
operator []()
重载[]
,重载C++中不存在的运算符是无效的
处理对象的函数建议在参数中使用引用&,因为能提高传递效率,但返回值不建议使用引用,因为函数内声明的对象是局部变量,后期被删除后将产生错误。
// 连续的加号是可行的
sum = a + b + c;
// 实质:
sum = a.operator+(b.operator+(c))
定义为一般函数
一般可以定义成成员函数,也可以定义成一般函数+友元
// 需要传递两个参数
friend Time operator+(Time &a, Time &b); // 使函数能够访问类的私有成员
Time operator+(Time &a, Time &b){...}
运算符重载实例
序号 | 运算符和实例 |
---|---|
1 | 一元运算符重载 |
2 | 二元运算符重载 |
3 | 关系运算符重载 |
4 | 输入/输出运算符重载 |
5 | ++ 和 – 运算符重载 |
6 | 赋值运算符重载 |
7 | 函数调用运算符 () 重载 |
8 | 下标运算符[]重载 |
9 | 类成员访问运算符 -> 重载 |
重载限制
- 运算符重载至少应有一个操作数是用户定义的类型(防止对基本类型进行重载)
- 运算符原有的句法规则不能变
- 运算符的优先级不变
- 不能创建新运算符
- 部分运算符只能通过成员函数进行重载:
=
、()
、[]
、->
- 部分运算符只能通过一般函数进行重载:
<<
- 部分运算符不能重载:
sizeof
、.
、.*
和(成员指针运算符)、::
、?:
等
类型转换情况
有时会出现Time a = 5 + b;
的情况,此时需要使用友元函数实现类型的自动转换。(见“经验:加法运算符的重载”)
示例:
class Time{
private:
int h;
int m;
int s;
public:
Time(int _h=0, int _m=0, int _s=0){
if(_h>=0&&_h<=24 && _m>=0&&_m<=60
&& _s>=0&&_s<=60){
h=_h;
m=_m;
s=_s;
}
else{
cout << "error" << endl;
return;
}
}
Time operator +(Time &t) const{
Time tmp;
tmp.s = (this->s+t.s)%60;
tmp.m = (this->m+t.m+(this->s+t.s)/60)%60;
tmp.h = (this->h+t.h+((this->m+t.m+
(this->s+t.s)/60)/60))%24;
return tmp;
}
void print(){ // ???????
cout << setw(2) << setfill('0')
<< h << ":" << m << ":" << s << endl;
}
~Time(){}
};
int main(){
Time a={12,32,11}, b={1,34,58};
Time c=a+b;
c.print();
return 0;
}
二、友元
基本使用方法
友元共3种,友元函数、友元类、友元成员函数,可赋予类成员函数相同的访问权限。
但在类中声明友元不意味着类函数可以调用这个友元函数,而友元函数访问权限与成员函数相同。
friend Time operator *(double m, const Time &t);
// 用非成员函数重载运算符
Time operator *(double m, const Time &t){
...
}
Time a;
Time c = 3.98 * a;
// 转换为operator*(3.98, a);
// 等效于a.operator*(3.98);
重载<<
运算符
void operator<<(ostream &os, const Time &t){
// ostream对象要使用引用,因为我们应该使用cout对象本身
os << t.hours << "," << t.minutes << endl;
}
// 以上调整顺序
// 其他ostream对象:cerr(错误流)、ofstream(文件)……
cout << t; // 使用
// 缺陷:cout << Time << "." << endl无法实现
重载<<
运算符(改进)
在ostream
中,operator<<
返回一个ostream
类,即cout
,所以可以连续书写。
ostream& operator<<(ostream &os, const Time &t){
...
return os;
}
三、再探运算符重载
当涉及到类定义类型转换时,使用非成员函数可能更好。(详情见最后一节)
实例:矢量类
(阅读示例代码11.13、11.14)(可自行书写vector.cpp并对照)
在类中如果声明了枚举,则全局使用时应如
namespace_name::class_name::enum_name
对类设计的启示:
我们可以通过使用枚举等形式为类确定模式,使得类可以适用于多个标准,如人民币、美元,公斤、英镑等,如:
class Weight{
public:
enum Mode {CNY, USD};
Weight(double n, Mode mode){
if(mode == CNY) {Yuan = n; Dollars = 0;}
else {Dollars = n; Yuan = 0;}
}
private:
double Yuan;
double Dollars;
};
一个运算符的多个重载,如减号与负号:
Vector operator-() const;
Vector operator-(const Vector b) const;
类的自动转换和强制类型转换
int *p = 10; // invalid int *p = (int *)(10); // valid
隐式类型转换
通过构造函数,此构造函数只能有一个形参或之后的形参都有默认值。
Day(int day);
explicit Day(int day); // 通过explicit关键字防止隐式类型转换的发生
Day d = 18;
Day d = (Day)18; // 有了explicit关键字,可以使用显式的强制类型转换
Day d = (Day)(17.22) // 允许,但需要转换无二义性
// 隐式类型转换时,有临时对象的生成和复制构造函数的调用
转换函数:类转换为其他数据类型
operator typename();
ClassName::operator int() const{
return int(ObjectName.a);
}
double a = (double) ObjectName; // 使用
double b = ObjectName;
// 如果不希望这种隐式类型转换,可以添加explicit关键字
// 另一种方法:添加功能相同的普通函数,如:
int ClassName::ClassToInt(){
return int(ObjectName.a);
}
// 如此一来,就只能使用int a = obj.ClassToInt()来实现转换了
该函数无返回值,是类方法,无参数。
经验:加法运算符的重载
Time::Time(int h){...}
Time a(2, 4, 5);
int b = 3;
Time c = a + b; // 重载为成员函数或友元函数时皆可用,转换为a.operator+(b)
Time c = b + a; // 重载为友元函数时才可用,因为b.operator(a)无效,无法进行类型转换
结论:重载加号运算符时优先使用友元函数