C++Primer 第14章-重载操作符与转换-课后习题答案
更新时间:2023-10-28 17:46:01 阅读量: 综合文库 文档下载
第十四章 重载操作符与转换
1。在什么情况下重载操作符与内置操作符不同?在什么情况下重载操作符与内置操作符相同?
重载操作符必须具有至少一个类类型或枚举类型的操作数。重载操作符不保证操作数的求值顺序,例如对 && 和 || 的重载版本不再具有“短路求值”的特性,两个操作数都要进行求值,而且不规定操作数的求值顺序。
对于优先级和结合性及操作数的数目都不变。
2。为Sales_item编写输入、输出。加以及复合赋值操作符的重载声明。 class Sales_item { friend std::istream& operator >> ( std::istream&, Sales_item& ); friend std::ostream& operator <<(std::ostream&, const Sales_item&); public: Sales_item& operator += ( const Sales_item& ); };
Sales_item operator+ ( const Sales_item&, const Sales_item& )
3。解释如下程序,假定Sales_item构造函数的参数是一个string,且不为explicit.解释如果构造函数为explicit会怎样。
string null_book = “0-000-00000-0”; Sales_item item(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类都定义了一个重载的==,可用于比较这些类的对象,指出下面的表达式中应用了哪个==版本。
string s; vector
“cobble” == “store” 应用了C++语言内置版本的重载== svec1[0] == svec2[0]; 应用了string版本的重载== svec1 == svec2 应用了vector版本的重载==
5。列出必须定义为类成员的操作符。
赋值 = , 下标[] , 调用 () ,成员访问箭头 ->
6。解释下面操作符是否应该为类成员,为什么?
(a)+ (b) += (c)++ (d) -> (e) << (f) && (g) == (h) ( )
+, <<, ==,&& 通常定义为非成员;->和()必须定义为成员,否则会出现编译错误;+= 和 ++ 会改变对象的状态,通常会定义为类成员。
7。为下面的ChecoutRecord类定义一个输出操作符: class CheckoutRecord {
public: // .. private: double book_id; string title; Date date_borrowed; Date date_due; pair
ostream&
operator << (ostream& out, const CheckoutRecord& s) { out << s.book_id << \ << s.title<< \ << s.date_borrowed<< \ << s.date_due << \ ; out<< \ <
8。在12.4节的习题中,你编写了下面某个类的框架: (b) Date 为所选择的类编写输出操作符。 #include
class Date {
public: Date() {} Date( int y, int m, int d ) { year = y; month = m; day = d; } friend ostream& operator<< ( ostream&, const Date& ); private: int year, month, day; };
ostream&
operator<< ( ostream& out, const Date& d ) { out << \ << \ << \}
int main() {
Date dt(1988, 12, 01); cout << dt << endl; system(\ return 0; }
9。给定下述输入,描述Sales_item输入操作符的行为。 (a) 0-201-99999-9 10 24.95 (b) 10 24.95 0-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 ) {
}
double price;
in >> s.isbn >> s.units_sold >> price; s.revenue = s.units_sold * price; return in;
如果将习题14.9中的数据作为输入,将会发生什么?
上述的输入操作符中 缺少了对错误输入情况的判断与处理,会导致错误的情况发生。 (a)的输入没有问题,但是(b)的输入,将形参Sales_item对象的ISBN成员值为10, units_sold和revenue成员保持原值不变。
11。为14.2.1节习题中定义的CheckoutRecord类定义一个输入操作符,确保该类操作符处理输入错误。
class CheckoutRecord {
public: // … friend istream& operator>>( istream&, CheckoutRecord& ); // 声明为类的友元 // … };
istream & operator>> ( istream& in, CheckoutRecord& c ) { cout << \; in >> c.book_id >> c.title; // Input Data data_borrowed and data_due cout << \; in >> c.date_borrowed; //.year >> c.date_borrowed.month >> c.date_borrowed.day; // Input Data data_due and data_due cout << \; in >> c.date_due;//.year >> c.date_due.month >> c.date_due.day; // Input the pair
}
{ pair
return in;
输入错误的情况:
输入正确的情况:
12。编写Sales_item操作符,用+进行实际加法,而+=调用+。与本节中操作符的实现方法相比较,讨论这个方法的缺点。
Sales_item Sales_item::operator+( const Sales_item& rhs ) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; }
将下面定义的非成员+=操作符声明为类Sales_item的友元: Sales_item operator+=( Sales_item& lhs, const Sales_item& rhs ) { lhs = lhs + rhs; return lhs; }
这个方法缺点:在+=操作中需要创建和撤销一个临时Sales_item对象,来保存+操作的结果,没有本节中的方法简单有效。
13。如果有,你认为Sales_item还应该有哪些其他的算术操作符?定义你认为的该类应包含的那些。
还应有-操作符,定义为类的非成员,相应地还应该有 -= 复合操作符并定义为类的成员。 Sales_item& Sales_item::operator-=( const Salse_item& rhs )
{ units_sold -= rhs.units_sold; revenue -= rhs.revenue; return *this; }
Sales_item operator-( const Sales_item &lhs, const Sales_item & rhs ) { Sales_item ret(lhs); ret -= rhs; return ret; }
14。定义一个赋值操作符,将isbn赋值给Sales_item对象。 Sales_item& Sales_item::operator=( const string & s ) { sbn = s; return *this; }
10。为14.2.1节习题中介绍的CheckoutRecord类定义赋值操作符。
主函数中定义了两个 CheckoutRecord 类的对象,调用了CheckoutRecord 类的 =赋值操作符,效果如下截图:
int _tmain(int argc, _TCHAR* argv[]) { CheckoutRecord c1; cin >> c1; CheckoutRecord c2; c2 = c1; // 调用了类的赋值操作符= std::cout << c2 << std::endl; // 输出对象c2的内容 system(\); return 0; }
// CheckoutRecord类中赋值操作符定义为: // 重载操作符=
CheckoutRecord& CheckoutRecord::operator =( const CheckoutRecord& cr ) { book_id = cr.book_id; title = cr.title; date_borrowed = cr.date_borrowed; // 前提:必须在Date类里也重载操作符= date_due = cr.date_due; // as before // 对pair进行赋值操作
}
borrower.first = cr.borrower.first;
borrower.second = cr.borrower.second;
// 对vector进行赋值操作 wait_list.clear(); // 首先清空
for ( vector< pair
return *this;
16。CheckoutRecord类还应该定义其他赋值操作符吗?如果是,解释哪些类型应该用作操作数并解释为什么。为这些类型实现赋值操作符。
从应用角度考虑,可能会修改预约时间date_due,可通过设置新的赋值操作符来实现;或者是往wait_list里添加排队读者,也可以通过设置新的赋值操作符来实现。 // set new date_due
CheckoutRecord& CheckoutRecord::operator=( const& new_due ) { date_due = new_due; return *this;
}
// add new readers who wait for some books
CheckoutRecord& CheckoutRecord::operator=( const std::pair
17。14.2.1节习题中定义了一个CheckoutRecord类,为该类定义一个下标操作符,从等待列表中返回一个名字。 下标重载:
pair
( const vector< pair
// 下标操作符重载
const pair
( const vector< pair
18。讨论用下标操作符实现这个操作的优缺点。 优点:使用简单。
缺点:操作的语义不够清楚,因为CheckoutRecord不是一个通常意义上的容器,而且等待者也不是CheckoutRecord容器中的一个元素,不易使人弄明白怎样用。
19。提出另一种方法定义这个操作。
可以将这个操作定义成普通的函数, pair
20。在ScreenPtr类的概略定义中,声明但没有定义赋值操作符,请实现ScreenPtr赋值操作符。
ScreenPtr& operator=( const ScreenPtr& sp ) { ++sp.ptr->use; if ( --ptr->use == 0 ) delete ptr; ptr = sp.ptr; return *this; }
21。定义一个类,该类保存一个指向ScreenPtr的指针。为该类定义一个重载的箭头操作符。 class NoName {
public: NoName( *p ): ps( new ScreenPtr(p) ) { } ScreenPtr operator->( ) { return *ps; } const ScreenPtr operator->( ) const { return *ps; } ~NoName() { delete ps; }
private: ScreenPtr *ps; };
22。智能指针可能应该定义相等操作符和不等操作符,以便测试两个指针是否相等或不等。将这些操作加入到ScreenPtr类。 class ScreenPtr {
public: // … friend inline bool operator==( const ScreenPtr &, const ScreenPtr &); friend inline bool operator!=( const ScreenPtr &, const ScreenPtr &); private: ScrPtr *ptr; };
// operator ==
inline bool operator==( const ScreenPtr &p1, const ScreenPtr &p2 ) { return p1.ptr == p2.ptr; }
// operator !=
inline bool operator!=( const ScreenPtr &p1, const ScreenPtr &p2 ) { return !( p1.ptr == p2-.ptr ); }
23。CheckedPtr 类表示指向数组的指针。为该类重载下标操作符和解引用操作符。使操作符确保CheckedPtr有效:它应该不可能对超出数组末端的元素进行解引用或索引。 // 下标操作符重载
int& CheckedPtr::operator[]( const size_t index ) { if ( beg + index >= end ) throw out_ot_range( “ invalid index“ ); return *( beg + index ); }
const int & CheckedPtr::operator[] ( const size_t index ) const { if ( beg + index >= end ) throw out_ot_range( “ invalid index“ ); return *( beg + index ); }
// 解引用操作符重载 int CheckedPtr::operator*() { if ( curr == end )
throw out_of_range( “ invalid current pointer” ); return *curr; }
const int& CheckedPtr::operator*() const { if ( curr == end ) throw out_of_range(“ invalid current pointer”); return *curr; }
24。习题14.23中定义的解引用操作符或下标操作符,是否也应该检查对数组起点之前的元素进行的解引用或索引?解释你的答案。
对于下标操作符,应该进行检查,因为当用户给出的下标索引值小于0时,编译器不会出现编译错误,而会出现运行时错误。应修改为: // 下标操作符重载
int& CheckedPtr::operator[]( const size_t index ) { if ( beg + index >= end || beg + index < beg ) throw out_ot_range( “ invalid index“ ); return *( beg + index ); }
const int & CheckedPtr::operator[] ( const size_t index ) const { if ( beg + index >= end || beg + index < beg ) throw out_ot_range( “ invalid index“ ); return *( beg + index ); }
而对于解引用操作符,返回curr所指向的数组元素,在创建对象时已经将curr初始化为指向数组的第一个元素,只有当执行—操作时才会对curr进行减的操作,而—操作符已经对curr的值与数组起点进行了检查,所以不用再在这里检查。
25。为了表现得像数组指针,CheckedPtr类应实现相等和关系操作符,以便确定两个CheckedPtr对象是否相等,或者一个小于另一个,诸如此类。为CheckedPtr类增加这些操作。 应将这些操作符声明为CheckedPtr的友元。 // 相等操作符
bool operator==( const CheckedPtr& lhs, const CheckedPtr& rhs ) { return lhs.beg == rhs.beg && lhs.end == rhs.end && lhs.curr == rhs.curr; }
bool operator!=( const CheckedPtr & lhs, const CheckedPtr& rhs ) { return !( lhs == rhs ); }
// 关系操作符
bool operator<( const CheckedPtr & lhs, const CheckedPtr& rhs ) { return lhs.beg == rhs.beg && lhs.end == rhs.end && lhs.curr < rhs.curr; }
bool operator>( const CheckedPtr & lhs, const CheckedPtr& rhs ) { return lhs.beg == rhs.beg && lhs.end == rhs.end && lhs.curr > rhs.curr; }
bool operator<=( const CheckedPtr & lhs, const CheckedPtr& rhs ) { return !( lhs.curr > rhs.curr ); }
bool operator >=( const CheckedPtr & lhs, const CheckedPtr& rhs ) { return !( lhs.curr < rhs.curr ); }
26。为CheckedPtr类定义加法或减法,以便这些操作符实现指针运算。 将这两个操作符声明为类的友元。
CheckedPtr operator+( const CheckedPtr& lhs, const size_t n ) { CheckedPtr temp( lhs ); temp.curr += n; if ( temp.curr > temp.end ) throw out_of_range(“ Too large n, over.”); return temp; }
CheckedPtr operator-( const CheckedPtr& lhs, const size_t n ) { CheckedPtr temp( lhs ); temp.curr -= n; if ( temp.curr < temp.beg ) throw out_of_range(“ Too large n, over.”); return temp; }
用到了复制构造函数,定义为:
CheckedPtr::CheckedPtr( const CheckedPtr& cp ): beg( cp.beg ), end( cp.end ), curr( cp.curr ) { }
27。讨论允许将空数组实参传给CheckedPtr构造函数的优缺点。
优点:构造函数的定义简单。缺点:导致构造的对象没有指向有效的数组,从而失去使用价值。应该在构造函数里控制参数确保beg 28。没有定义自增和自减操作符的const版本,为什么? const所修饰的operator,不可以改变对象,而自增和自减肯定改变了对象,与const意义相反,所以不定义const版本。 29。我们也没有实现箭头操作符,为什么? 因为此类所指向的是int型数组,int型为内置类型,而箭头操作符必须返回一个类类型的指针,所以不实现。 30。定义一个CheckedPtr版本,保存Screen数组。为该类实现重载的自增、自减。解引用和箭头等操作符。 class CheckedPtr { public: CheckedPtr( Screen *b, Screen *e ): beg( b ), end( e ), curr( b ) { } // 自增、自减 CheckPtr& operator++(); CheckPtr& operator—(); CheckPtr& operator++( int ); CheckPtr& operator—( int ); // 箭头操作符 Screen* operator->(); const Screen* operator->() const; // 解引用操作 Screen& operator*(); const Screen& operator*(); private: Screen *beg; Screen *end; Screen *curr; }; CheckPtr& CheckPtr::operator++() { if ( curr == end ) throw out_of_range( “increment past the end of CheckedPtr”); ++curr; return *this; } CheckPtr& CheckPtr::operator—() { if ( curr == begin ) throw out_of_range( “decrement past the beginning of CheckedPtr”); --curr; return *this; } // 后缀式 CheckPtr& CheckPtr::operator++( int ) { CheckedPtr temp( *this ); ++*this; return temp; } CheckPtr& CheckPtr::operator—( int ) { CheckedPtr temp( *this ); --*this; return temp; } // 箭头操作符 Screen* CheckedPtr::operator->() { return curr; } const Screen* CheckedPtr::operator->() const { return curr; } Screen& CheckedPtr::operator*() { if ( curr == end ) throw out_of_range( “invalid current pointer.”); return *curr; } const Screen& CheckedPtr::operator*() const { if ( curr == end ) throw out_of_range( “invalid current pointer.”); return *curr; } 31。定义一个函数对象执行“如果??则??否则”操作;该函数对象应接受三个形参;它应该测试第一个形参;如果测试成功,则返回第二个形参;否则,返回第三个形参。 class NoName { public: NoName() { } NoName( int i1, int i2, int i3 ): iVal1( i1 ), iVal2( i2 ), iVal3( i3) { } int operator() ( int i1, int i2, int i3 ) { return i1? i2: i3; } private: int iVal1; int iVal2; int iVal3; }; 32。一个重载的函数调用操作符可以接受多少个操作数? 0个或多个。 33。使用标准库算法和GT_cls类,编写一个程序查找序列中第一个比指定值大的元素。 // 14.33_Gt_cls_and_algorithm.cpp : 定义控制台应用程序的入口点。 // #include \#include class GT_cls { public: GT_cls( const string gW = \ ) : givenWord( gW ) { } bool operator() ( const string &s ) { return ( s > givenWord ); } private: std::string givenWord; }; int _tmain(int argc, _TCHAR* argv[]) { std::cout << \ << std::endl; vector 34。编写类似于GT_cls的类,但测试两个值是否相等。使用该对象和标准库算法编写程序,替换序列中给定值的所有实例。 // 14.34_GT_cls.cpp : 定义控制台应用程序的入口点。 #include \#include class GT_cls { public: GT_cls( int iVal = 0 ) : val( iVal ) { } bool operator() ( const int & iv ) { return ( iv == val ); } private: int val; }; int _tmain(int argc, _TCHAR* argv[]) { std::cout << \ << std::endl; vector } cin.clear(); cout << \; int rp; cin >> rp; cout << \; int givenNum; cin >> givenNum; // replace replace_if( iVec.begin(), iVec.end(), GT_cls( rp ), givenNum ); cout << \; for ( vector system(\); return 0; 35。编写类似于GT_cls的类,但测试给定string对象的长度是否与其边界相匹配。报告输入中有多少单词的长度在1到10之间。 // 14.35_BT_cls.cpp : 定义控制台应用程序的入口点。 // #include \#include class BT_cls { public: BT_cls( size_t len1 = 0, size_t len2 = 0 ) { if ( len1 < len2 ) { minlen = len1; maxlen = len2; } else { minlen = len2; maxlen = len1; } } bool operator() ( const string &s ) { return ( s.size() >= minlen && s.size() <= maxlen ); } private: std::string::size_type minlen, maxlen; }; bool isShorter( const string &s1, const string &s2 ) { return s1.size() < s2.size(); } int _tmain(int argc, _TCHAR* argv[]) { std::cout << \ << std::endl; vector
- 多层物业服务方案
- (审判实务)习惯法与少数民族地区民间纠纷解决问题(孙 潋)
- 人教版新课标六年级下册语文全册教案
- 词语打卡
- photoshop实习报告
- 钢结构设计原理综合测试2
- 2014年期末练习题
- 高中数学中的逆向思维解题方法探讨
- 名师原创 全国通用2014-2015学年高二寒假作业 政治(一)Word版
- 北航《建筑结构检测鉴定与加固》在线作业三
- XX县卫生监督所工程建设项目可行性研究报告
- 小学四年级观察作文经典评语
- 浅谈110KV变电站电气一次设计-程泉焱(1)
- 安全员考试题库
- 国家电网公司变电运维管理规定(试行)
- 义务教育课程标准稿征求意见提纲
- 教学秘书面试技巧
- 钢结构工程施工组织设计
- 水利工程概论论文
- 09届九年级数学第四次模拟试卷
- 操作符
- 重载
- 课后
- 习题
- 转换
- 答案
- Primer
- 企业管理案例分析 麦当劳
- 郭锡良古代汉语之授课教案
- 2019精选中考物理题型训练二动态电路和电路故障专题复习题
- 工程流体力学课后习题答案1-3
- 成本会计实训题
- 2016年中考文言文复习-《陋室铭》(含答案)
- 第五批无锡市中小学教学能手名单
- 维修电工高级技师试题库(包括隐藏)判断、选择
- 东北财经大学导学资料微观经济学
- 第04章 单基因病
- 计量经济学实验
- 作业 - 图文
- 330kV变电站巡视标准化作业指导书 - 图文
- 加强基层党员干部队伍建设研究
- 第一单元练习 - 图文
- 可控源音频大地电磁法(CSAMT)勘查设计
- 改革开放30年沈阳的变化
- 苏教版三年级上册数学第一二三四五六七八九十单元试卷
- 施耐德变频器ATV61&71的ModBus通讯方法
- 水处理工程技术试卷试题下载