使用类
运算符重载
如果需要重载运算符,需要使用被称为运算符函数的特殊函数形式。运算符函数的格式如下:
operator op(argument_list); //op是有效的C++运算符,不能虚构一个新的符号。
例如:
operator +(); //重载+运算符
operator *(); //重载*运算符
如果有一个类Salesperson,定义了一个operator +()成员函数,那么:
district2 = sid + sara; //sid和sara是Salesperson对象。
//等价于:
district2 = sid.operator+(sara);
使用+运算符表示法,就不必使用笨拙的函数表示法。
int a, b, c;
Time A, B, C;
c = a+b;
C = A+B;
...
t4 = t1 + t2 + t3;
//等价于:
t4 = t1.operator+(t2 + t3);
t4 = t1.operator+(t2.operator+(t3));
重载限制:
- 重载后的运算符必须至少有一个操作数是用户定义的类型。
- 使用运算符时不能违反运算符原来的语法规则。例如,不能将求模运算符重载成使用一个操作数的。
- 不能修改运算符的优先级。
- 不能创建新运算符。
- 不能重载下面的运算符
- P316页
- = () [] ->必须通过成员函数重载
友元
友元有三种:
- 友元函数;
- 友元类;
- 友元成员函数;
此处是友元函数
通过让函数称为类的友元,可以赋予函数与类成员函数相同的访问权限。
何时需要友元?
如果有一个类重载了乘法运算符,例如可以进行以下的运算:
A=B*2.7;
//相当于:
A=B.operator*(2.7)
虽然*
是一个阿贝尔群,即2.7*B
和B*2.7
是一样的,但是显然第一个对于只重载了乘法运算符的程序而言是错误的。因为2.7不是和B一样的对象。
解决方案:
- 告知每个人,只能按照
B*2.7
这种写法写。 对服务器友好-客户警惕(server-friendly,client-beware)的解决方案。 - 使用友元函数。
友元函数创建:
将如下声明放在类声明中:
friend Time operator*(double m, const Time *t);
- 不是成员函数,不能使用成员运算符调用;
- 虽然不是成员函数,但是与成员函数的访问权限相同。
定义时不需要使用friend关键字,定义如下:
Time operator*(double m, const Time & t){
...
}
同时定义的时候也不需要加类的限定符。
友元函数同样在声明的时候定义也会创建为内联函数。
重载运算符:作为成员函数还是非成员函数?
Time operator+(const Time & t) const;
...
friend Time operator+(const Time & t1, const Time & t2);
上述两个声明的版本,对于成员函数的版本来说,一个操作数通过this指针隐式地传递,另一个操作数则作为函数参数显示第传递。对于友元版本来说,两个操作数都作为参数来传递的。
注:只能选择两个中的一个,而不能同时选择这两种格式,同时定义这两种格式将被视为二义性错误。
类的自动转换和强制类型转换
类型转换
如果在定义构造函数的时候,构造函数只有一个参数,例如:
class Stonewt{
private:
enum {Lbs_per_stn=14};
int stone;
double pds_left;
double pounds;
public:
Stonewt(double lbs);
Stonewt(int stn, double lbs);
Stonewt();
...
};
如果执行一下代码:
Stonewt myCat;
myCat = 19.6;
程序将使用构造函数Stonewt(double)
来创建一个临时的Stonewt
对象,并将19.6
作为初始化的值。随后采用逐成员赋值方式将该临时对象的内容赋值到myCat
中。
如果在多个参数的构造函数中使用默认参数,使得只有一个参数不是默认参数也可以使用隐式转换。
explicit
通过在构造函数前加上explicit
关键字可以关闭该构造函数的隐式转换,但是可以使用显示转换使用该构造函数。
隐式转换:
- 将
Stonewt
对象初始化为double
值时。 - 将
double
赋值给Stonewt
对象时。 - 将
double
值传递给接受Stonewt
参数的函数时。 - 返回值被声明为
Stonewt
的函数试图返回double
值时。 - 在上述任意一种情况下,使用可转换为
double
类型的内置类型时。
对于最后一条,如果还存在一个构造函数Stonewt(long)
那么程序可能会出现二义性错误。
转换函数
之前的类型转换都是将其他类型转换为对象,并通过构造函数实现。
但是如果想将对象转换为其他类型,则需要使用以下转换函数:
operator typeName();
- 转换函数必须是类方法;
- 转换函数不能指定返回类型;
- 转换函数不能有参数;
例如,转换为double
类型的函数的原形如下:
operator double();
···
Stonewt::operator double(){
return pounds;
}
double的位置指出要转换的类型,因此不需要返回类型。不需要参数。
在函数定义中只需要返回一个与之对应的值即可。
自动应用类型转换
如果同时定义了int()
和double()
函数,在使用cout
输出的时候,程序会出现二义性错误,但是如果仅定义了其中一个,则程序不会出现二义性错误。
如果定义了两种或者更多种的可能产生二义性错误的转换时,可以将该对象先强制转换为其中一个,然后程序在自动将其转换为目标类型。
在C++11中,explicit
可以用于转换函数。
转换函数和友元函数
要让double变量和Stonewt变量相加,有两种选择。
-
定义友元函数,让Stonewt(double)构造函数将double类型的参数转换为Stonewt类型的参数。
-
将加法运算符重载为一个限时使用double类型参数的函数
第一种方法速度稍慢,但是出错的概率小同时代码短,第二种方法代码长,但是运行速度快。