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 svec1, svec2;

“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 borrower; vector< pair* > wait_list; };

ostream&

operator << (ostream& out, const CheckoutRecord& s) { out << s.book_id << \ << s.title<< \ << s.date_borrowed<< \ << s.date_due << \ ; out<< \ <* >::const_iterator it = s.wait_list.begin(); it != s.wait_list.end(); ++it ) { out << \ << (*it)->first << \ << (*it)->second << endl; } return out; }

8。在12.4节的习题中,你编写了下面某个类的框架: (b) Date 为所选择的类编写输出操作符。 #include using namespace std;

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 borrower cout << \; in >> c.borrower.first >> c.borrower.second; if ( !in ) { c = CheckoutRecord(); return in; } // Input wait_list cout << \; c.wait_list.clear(); while ( in )

}

{ pair *ppr = new pair; in >> ppr->first >> ppr->second; if ( !in ) { delete ppr; return in; } c.wait_list.push_back( ppr ); }

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* >::const_iterator it = cr.wait_list.begin(); it != cr.wait_list.end(); ++ it ) { pair *ppr = new pair; ppr->first = (*it)->first; ppr->second = (*it)->second; wait_list.push_back( ppr ); }

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& new_waiter ) { pair *ppr = new pair; *ppr = new_waiter; wait_list.push_back( ppr ); return *this; }

17。14.2.1节习题中定义了一个CheckoutRecord类,为该类定义一个下标操作符,从等待列表中返回一个名字。 下标重载:

pair& CheckoutRecord::operator[ ]

( const vector< pair* >::size_type index ) { return *wait_list.at( index ); }

// 下标操作符重载

const pair& CheckoutRecord::operator[ ]

( const vector< pair* >::size_type index ) const { return *wait_list.at( index ); }

18。讨论用下标操作符实现这个操作的优缺点。 优点:使用简单。

缺点:操作的语义不够清楚,因为CheckoutRecord不是一个通常意义上的容器,而且等待者也不是CheckoutRecord容器中的一个元素,不易使人弄明白怎样用。

19。提出另一种方法定义这个操作。

可以将这个操作定义成普通的函数, pair& get_a_waiter( const size_t index ) 和 const pair& get_a_waiter ( const size_t index ) const.

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 #include #include #include using namespace std;

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 text; string word; while ( std::cin >> word ) { text.push_back( word ); } // end of input the text // input the given word std::cin.clear(); std::cout << \; string gWord; std::cin >> gWord; // deal with text , to realize find the first word which is bigger than the given word vector::iterator it = find_if( text.begin(), text.end(), GT_cls( gWord )); if ( it != text.end() ) std::cout << \ << std::endl << \ << std::endl << \ << *it << std::endl; system(\); return 0; }

34。编写类似于GT_cls的类,但测试两个值是否相等。使用该对象和标准库算法编写程序,替换序列中给定值的所有实例。

// 14.34_GT_cls.cpp : 定义控制台应用程序的入口点。

#include \#include #include #include #include using namespace std;

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 iVec; int iVal; while ( std::cin >> iVal) { iVec.push_back( iVal ); } // end of input the iVec

}

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::iterator it = iVec.begin(); it != iVec.end(); ++it ) { cout << *it << \; }

system(\); return 0;

35。编写类似于GT_cls的类,但测试给定string对象的长度是否与其边界相匹配。报告输入中有多少单词的长度在1到10之间。

// 14.35_BT_cls.cpp : 定义控制台应用程序的入口点。 //

#include \#include #include #include #include using namespace std;

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 text; string word; while ( std::cin >> word ) { text.push_back( word ); } // end of input the text // deal with text sort( text.begin(), text.end() ); text.erase( unique( text.begin(), text.end() ), text.end() ); stable_sort( text.begin(), text.end(), isShorter );

本文来源:https://www.bwwdw.com/article/r942.html

Top