第14章 重载操作符与转换
【95】14.1在什么情况下重载操作符与内置操作符不同?在什么情况下重载操作符与内置操作符相同?
答:重载操作符与内置操作符的不同之处在于:重载操作符必须具有至少一个类类型或枚举类型的操作数;重载操作符不保证操作数的求值顺序,例如,&&和||的重载版本就失去了“短路求值“特性,两个操作数都要进行求值,而且不规定操作数的求值顺序。
重载操作符与内置操作符的相同之处在于:操作符的优先级、结合性及操作数数目均相同。
【96】14.2为Sales_item编写输入、输出,加以及复合赋值操作符的重载声明
答:Class Sales_item{
friendstd::istream& operator>> (std::istream&,Sales_item&);
friendstd::ostream& operator<< (std::ostream&,const Sales_item&);
public:
Sales_item&operator+=(const Sales_item&);
};
Sales_itemoperator+(const Sales_item&,const Sales_item&);
其中,复合赋值操作符定义为public成员,输入和输出操作符需要访问Sales_item类成员,所以需定义为Sales_item类的友元。加操作符可以用public成员+=来实现,所以无需定义为Sales_item类的友元。
【97】14.5列出必须定义为类成员的操作符
答:赋值=、下标[]、调用()、成员访问箭头->等操作符必须定义为类成员;
【98】14.6解释下面操作符是否应该为类成员,为什么?
+、--、++、>、<<、&&、==、()
答:+、<<和==操作符通常应定义为非成员函数,但<<操作符通常需要访问类的数据成员,所以一般应指定为类的友元;
--和++会改变对象的状态,通常应定义为类成员;
->和()必须定义为类成员,否则会出现编译错误;
&&一般对类类型操作数没有意义,通常不进行重载;如果一定要重载,可重载为非成员函数;
【99】14.8定义Date类的输出操作符
答: ostream& operator <<(ostream& out,const Date d)
{
out<
returnout;
}
注意,应将<<操作符指定为Date类的友元。
【100】14.14定义一个赋值操作符,将isbn复制给Sales_item对象
答:Sales_item& Sales_item::operator(conststring& str)
{
isbn=str;
return*this;
}
注意,赋值操作符必须定义为类的成员函数,且一般应返回左操作数的引用。
【101】14.17为CheckoutRecord类定义一个下标操作符,从等待列表中返回一个名字。
答:pair
{
return*wait_list.at(index); //使用at可检查下标是否越界
}
constpair
{
return*wait_list.at(index);
}
注意,下标操作符必须定义为类成员函数,且返回引用以便可以用在赋值操作符的任意一边。类定义下标操作符时,一般需定义两个版本,即返回引用的非const成员及返回const引用的const成员,以便可以对const和非const对象使用下标;可以对下标是否越界进行检查(这与内置下标操作符的语义有所不同),以避免对内存的非法访问。
【102】14.21定义一个类,该类保存一个指向ScreenPtr的指针。为该类定义一个重载的箭头操作符。
答:class NoName{
public:
NoName(Screen*p):ptr(new ScreenPtr(p)){}
ScreenPtroperator->()
{
return*ptr;
}
constScreenPtr operator->() const
{
return*ptr;
}
private:
ScreenPtr*ptr;
};
注意,箭头操作符必须定义为类成员函数,重载的箭头操作符不接受显式形参,且必须返回指向类类型的指针,或者返回定义了箭头操作符的类类型对象;需要定义箭头操作符的const和非const版本,以便可以对const和非const对象使用箭头操作符。
【103】14.41解释这两个转换操作符之间的不同
classintegral{
public:
operatorconst int() const;
operatorint() const;
}
这两个转换操作符是否太严格了?如果是,怎样使得转换更通用一些?
答:这两个转换操作符之间的不同之处在于:前者将对象转换为const int值(int型const变量),而后者将对象转换为int值(int型变量);前者太严格了,只能用于可以使用const int值的地方,将前者去掉只保留后者,即可使得转换更为适用。(事实上,如果这两个转换操作符同时存在,则在既可使用int型const变量又可使用int型变量的情况下,会因编译器无法作出抉择而产生错误)。
【104】14.44为下述每个初始化列出可能的类类型转换序列。每个初始化的结果是什么?
classLongDouble{
public:
operatordouble();
operatorfloat();
};
LongDoubleidObj;
a)intex1=idObj; b)floatex2=idObj;
答:a)有二义性,因为既可以先使用从LongDouble到double的转换操作,再使用从double到int的标准转换,也可以先使用从LongDouble到float的转换操作,再使用从float到int的标准转换,二者没有优劣之分。
b)使用从LongDouble到float的转换操作,将idObj对象转换为float值用于初始化ex2;
【105】14.45哪个calc()函数是如下函数调用的最佳可行函数?列出调用每个函数所需的转换序列,并解释为什么所选定的就是最佳可行函数?
classLongDouble{
public:
LongDouble(double);
……
};
voidcalc(int);
voidcalc(LongDouble);
doubledval;
calc(dval);//which function?
答:最佳可行函数为void calc(int)。调用void calc(int)所需的转换为:将实参dval由double类型转换为int类型(标准转换);
调用void calc(LongDouble所需的转换为:将实参dval有double类型转换为LongDouble类型(使用LongDouble类的构造函数,为类类型转换);
因为标准转换优于类类型转换,所以void calc(int)为最佳可行函数。