C++ Primer Plus 第十一章 使用类

使用类

运算符重载

如果需要重载运算符,需要使用被称为运算符函数的特殊函数形式。运算符函数的格式如下:

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*BB*2.7是一样的,但是显然第一个对于只重载了乘法运算符的程序而言是错误的。因为2.7不是和B一样的对象。

解决方案:

  1. 告知每个人,只能按照B*2.7这种写法写。 对服务器友好-客户警惕(server-friendly,client-beware)的解决方案。
  2. 使用友元函数。

友元函数创建:

将如下声明放在类声明中:

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类型参数的函数

第一种方法速度稍慢,但是出错的概率小同时代码短,第二种方法代码长,但是运行速度快。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值