C++Primer第4版习题解答十四章.docx
《C++Primer第4版习题解答十四章.docx》由会员分享,可在线阅读,更多相关《C++Primer第4版习题解答十四章.docx(30页珍藏版)》请在冰豆网上搜索。
![C++Primer第4版习题解答十四章.docx](https://file1.bdocx.com/fileroot1/2023-1/22/9e3de0ea-c619-4602-9745-24602e673abf/9e3de0ea-c619-4602-9745-24602e673abf1.gif)
C++Primer第4版习题解答十四章
第十四章重载操作符与转换
1。
在什么情况下重载操作符与内置操作符不同?
在什么情况下重载操作符与内置操作符相同?
重载操作符必须具有至少一个类类型或枚举类型的操作数。
重载操作符不保证操作数的求值顺序,例如对&&和||的重载版本不再具有“短路求值”的特性,两个操作数都要进行求值,而且不规定操作数的求值顺序。
对于优先级和结合性及操作数的数目都不变。
2。
为Sales_item编写输入、输出。
加以及复合赋值操作符的重载声明。
classSales_item
{
friendstd:
:
istream&operator>>(std:
:
istream&,Sales_item&);
friendstd:
:
ostream&operator<<(std:
:
ostream&,constSales_item&);
public:
Sales_item&operator+=(constSales_item&);
};
Sales_itemoperator+(constSales_item&,constSales_item&)
3。
解释如下程序,假定Sales_item构造函数的参数是一个string,且不为explicit.解释如果构造函数为explicit会怎样。
stringnull_book=“0-000-00000-0”;
Sales_itemitem(cin);
item+=null_book;
第一句:
调用接受一个C风格的字符串形参的string构造函数,创建一个string临时对象,然后使用string复制构造函数用这个临时对象初始化string对象null_book,
第二句:
从标准输入设备读入数据,创建Sales_item对象item。
第三句:
首先调用接受一个string参数的Sales_item构造函数,创建一个临时对象,然后调用Sales_item的复合重载操作符+=,将这个Sales_item临时对象加到item对象上,
如果构造函数为explicit,则不能进行从string对象到Sales_item对象的隐式转换,第三句将不能被编译。
4。
string和vector类都定义了一个重载的==,可用于比较这些类的对象,指出下面的表达式中应用了哪个==版本。
strings;vectorsvec1,svec2;
“cobble”==“store”应用了C++语言内置版本的重载==
svec1[0]==svec2[0];应用了string版本的重载==
svec1==svec2应用了vector版本的重载==
5。
列出必须定义为类成员的操作符。
赋值=,下标[],调用(),成员访问箭头->
6。
解释下面操作符是否应该为类成员,为什么?
(a)+(b)+=(c)++(d)->(e)<<(f)&&(g)==(h)()
+,<<,==,&&通常定义为非成员;->和()必须定义为成员,否则会出现编译错误;+=和++会改变对象的状态,通常会定义为类成员。
7。
为下面的ChecoutRecord类定义一个输出操作符:
classCheckoutRecord
{
public:
//..
private:
doublebook_id;
stringtitle;
Datedate_borrowed;
Datedate_due;
pairborrower;
vector*>wait_list;
};
ostream&
operator<<(ostream&out,constCheckoutRecord&s)
{
out<<<"\t";
out<<"borrower:
"<out<<"wait_list:
"<for(vector*>:
:
const_iteratorit=s.wait_list.begin();
it!
=s.wait_list.end();++it)
{
out<<"\t"<<(*it)->first<<","<<(*it)->second<}
returnout;
}
8。
在12.4节的习题中,你编写了下面某个类的框架:
(b)Date
为所选择的类编写输出操作符。
#include
usingnamespacestd;
classDate
{
public:
Date(){}
Date(inty,intm,intd)
{
year=y;month=m;day=d;
}
friendostream&operator<<(ostream&,constDate&);
private:
intyear,month,day;
};
ostream&
operator<<(ostream&out,constDate&d)
{
out<<"year:
"<<<"month:
"<<<"day:
"<}
intmain()
{
Datedt(1988,12,01);
cout<
system("pause");
return0;
}
9。
给定下述输入,描述Sales_item输入操作符的行为。
(a)0-201-99999-91024.95
(b)1024.950-201-99999-9
(a)将0-201-99999-9读入赋给对象的isbn成员,将10给units_sold成员,revenue成员被设置为249.5
(b)首先将形参对象的isbn成员设置为10,然后因为输入的数据不符合要求,导致输入失败,从而执行else语句,将Sales_item对象复位为空对象,此时isbn为空string,units_sold和revenue都为0
10。
下述Sales_item输入操作符有什么错误?
istream&operator>>(istream&in,Sales_item&s)
{
doubleprice;
in>>s.isbn>>s.units_sold>>price;
s.revenue=s.units_sold*price;
returnin;
}
如果将习题14.9中的数据作为输入,将会发生什么?
上述的输入操作符中缺少了对错误输入情况的判断与处理,会导致错误的情况发生。
(a)的输入没有问题,但是(b)的输入,将形参Sales_item对象的ISBN成员值为10,units_sold和revenue成员保持原值不变。
11。
为14.2.1节习题中定义的CheckoutRecord类定义一个输入操作符,确保该类操作符处理输入错误。
classCheckoutRecord
{
public:
//…
friendistream&operator>>(istream&,CheckoutRecord&);//声明为类的友元
//…
};
istream&operator>>(istream&in,CheckoutRecord&c)
{
cout<<"Inputbookid(double)andtitle(string):
\n";
in>>c.book_id>>c.title;
//InputDatadata_borrowedanddata_due
cout<<"Inputdata_borrowed(3ints:
year,month,day):
\n";
in>>c.date_borrowed;//.year>>c.date_borrowed.month>>c.date_borrowed.day;
//InputDatadata_dueanddata_due
cout<<"Inputdata_due(3ints:
year,month,day):
\n";
in>>c.date_due;//.year>>c.date_due.month>>c.date_due.day;
//Inputthepairborrower
cout<<"Inputthepairborrower(string):
\n";
in>>c.borrower.first>>c.borrower.second;
if(!
in)
{
c=CheckoutRecord();
returnin;
}
//Inputwait_list
cout<<"Inputthewait_list(string):
\n";
c.wait_list.clear();
while(in)
{
pair*ppr=newpair;
in>>ppr->first>>ppr->second;
if(!
in)
{
deleteppr;
returnin;
}
c.wait_list.push_back(ppr);
}
returnin;
}
输入错误的情况:
输入正确的情况:
12。
编写Sales_item操作符,用+进行实际加法,而+=调用+。
与本节中操作符的实现方法相比较,讨论这个方法的缺点。
Sales_itemSales_item:
:
operator+(constSales_item&rhs)
{
units_sold+=rhs.units_sold;
revenue+=rhs.revenue;
return*this;
}
将下面定义的非成员+=操作符声明为类Sales_item的友元:
Sales_itemoperator+=(Sales_item&lhs,constSales_item&rhs)
{
lhs=lhs+rhs;
returnlhs;
}
这个方法缺点:
在+=操作中需要创建和撤销一个临时Sales_item对象,来保存+操作的结果,没有本节中的方法简单有效。
13。
如果有,你认为Sales_item还应该有哪些其他的算术操作符?
定义你认为的该类应包含的那些。
还应有-操作符,定义为类的非成员,相应地还应该有-=复合操作符并定义为类的成员。
Sales_item&Sales_item:
:
operator-=(constSalse_item&rhs)
{
units_sold-=rhs.units_sold;
revenue-=rhs.revenue;
return*this;
}
Sales_itemoperator-(constSales_item&lhs,constSales_item&rhs)
{
Sales_itemret(lhs);
ret-=rhs;
returnret;
}
14。
定义一个赋值操作符,将isbn赋值给Sales_item对象。
Sales_item&Sales_item:
:
operator=(conststring&s)
{
sbn=s;
return*this;
}
10。
为14.2.1节习题中介绍的CheckoutRecord类定义赋值操作符。
主函数中定义了两个CheckoutRecord类的对象,调用了CheckoutRecord类的=赋值操作符,效果如下截图:
int_tmain(intargc,_TCHAR*argv[])
{
CheckoutRecordc1;
cin>>c1;
CheckoutRecordc2;
c2=c1;//调用了类的赋值操作符=
std:
:
cout<:
endl;//输出对象c2的内容
system("pause");
return0;
}
//CheckoutRecord类中赋值操作符定义为:
//重载操作符=
CheckoutRecord&CheckoutRecord:
:
operator=(constCheckoutRecord&cr)
{
book_id=cr.book_id;
title=cr.title;
date_borrowed=cr.date_borrowed;//前提:
必须在Date类里也重载操作符=
date_due=cr.date_due;//asbefore
//对pair进行赋值操作
borrower.first=cr.borrower.first;
borrower.second=cr.borrower.second;
//对vector进行赋值操作
wait_list.clear();//首先清空
for(vector*>:
:
const_iteratorit=cr.wait_list.begin();
it!
=cr.wait_list.end();++it)
{
pair*ppr=newpair;
ppr->first=(*it)->first;
ppr->second=(*it)->second;
wait_list.push_back(ppr);
}
return*this;
}
16。
CheckoutRecord类还应该定义其他赋值操作符吗?
如果是,解释哪些类型应该用作操作数并解释为什么。
为这些类型实现赋值操作符。
从应用角度考虑,可能会修改预约时间date_due,可通过设置新的赋值操作符来实现;或者是往wait_list里添加排队读者,也可以通过设置新的赋值操作符来实现。
//setnewdate_due
CheckoutRecord&CheckoutRecord:
:
operator=(const&new_due)
{
date_due=new_due;
return*this;
}
//addnewreaderswhowaitforsomebooks
CheckoutRecord&CheckoutRecord:
:
operator=(conststd:
:
pair&new_waiter)
{
pair*ppr=newpair;
*ppr=new_waiter;
wait_list.push_back(ppr);
return*this;
}
17。
14.2.1节习题中定义了一个CheckoutRecord类,为该类定义一个下标操作符,从等待列表中返回一个名字。
下标重载:
pair&CheckoutRecord:
:
operator[]
(constvector*>:
:
size_typeindex)
{
return*wait_list.at(index);
}
//下标操作符重载
constpair&CheckoutRecord:
:
operator[]
(constvector*>:
:
size_typeindex)const
{
return*wait_list.at(index);
}
18。
讨论用下标操作符实现这个操作的优缺点。
优点:
使用简单。
缺点:
操作的语义不够清楚,因为CheckoutRecord不是一个通常意义上的容器,而且等待者也不是CheckoutRecord容器中的一个元素,不易使人弄明白怎样用。
19。
提出另一种方法定义这个操作。
可以将这个操作定义成普通的函数,pair&get_a_waiter(constsize_tindex)和constpair&get_a_waiter(constsize_tindex)const.
20。
在ScreenPtr类的概略定义中,声明但没有定义赋值操作符,请实现ScreenPtr赋值操作符。
ScreenPtr&operator=(constScreenPtr&sp)
{
++sp.ptr->use;
if(--ptr->use==0)
deleteptr;
ptr=sp.ptr;
return*this;
}
21。
定义一个类,该类保存一个指向ScreenPtr的指针。
为该类定义一个重载的箭头操作符。
classNoName
{
public:
NoName(*p):
ps(newScreenPtr(p)){}
ScreenPtroperator->()
{
return*ps;
}
constScreenPtroperator->()const
{
return*ps;
}
~NoName()
{
deleteps;
}
private:
ScreenPtr*ps;
};
22。
智能指针可能应该定义相等操作符和不等操作符,以便测试两个指针是否相等或不等。
将这些操作加入到ScreenPtr类。
classScreenPtr
{
public:
//…
friendinlinebooloperator==(constScreenPtr&,constScreenPtr&);
friendinlinebooloperator!
=(constScreenPtr&,constScreenPtr&);
private:
ScrPtr*ptr;
};
//operator==
inlinebooloperator==(constScreenPtr&p1,constScreenPtr&p2)
{
returnp1.ptr==p2.ptr;
}
//operator!
=
inlinebooloperator!
=(constScreenPtr&p1,constScreenPtr&p2)
{
return!
(p1.ptr==p2-.ptr);
}
23。
CheckedPtr类表示指向数组的指针。
为该类重载下标操作符和解引用操作符。
使操作符确保CheckedPtr有效:
它应该不可能对超出数组末端的元素进行解引用或索引。
//下标操作符重载
int&CheckedPtr:
:
operator[](constsize_tindex)
{
if(beg+index>=end)
throwout_ot_range(“invalidindex“);
return*(beg+index);
}
constint&CheckedPtr:
:
operator[](constsize_tindex)const
{
if(beg+index>=end)
throwout_ot_range(“invalidindex“);
return*(beg+index);
}
//解引用操作符重载
intCheckedPtr:
:
operator*()
{
if(curr==end)
throwout_of_range(“invalidcurrentpointer”);
return*curr;
}
constint&CheckedPtr:
:
operator*()const
{
if(curr==end)
throwout_of_range(“invalidcurrentpointer”);
return*curr;
}
24。
习题14.23中定义的解引用操作符或下标操作符,是否也应该检查对数组起点之前的元素进行的解引用或索引?
解释你的答案。
对于下标操作符,应该进行检查,因为当用户给出的下标索引值小于0时,编译器不会出现编译错误,而会出现运行时错误。
应修改为:
//下标操作符重载
int&CheckedPtr:
:
operator[](constsize_tindex)
{
if(beg+index>=end||beg+indexthrowout_ot_range(“invalidindex“);
return*(beg+index);
}
constint&CheckedPtr:
:
operator[](constsize_tindex)const
{
if(beg+index>=end||beg+indexthrowout_ot_range(“invalidindex“);
return*(beg+index);
}
而对于解引用操作符,返回curr所指向的数组元素,在创建对象时已经将curr初始化为指向数组的第一个元素,只有当执行—操作时才会对curr进行减的操作,而—操作符已经对curr的值与数组起点进行了检查,所以不用再在这里检查。
25。
为了