《C++ Primer》第15章 面向对象程序设计
15.8节 容器与继承 习题答案
练习15.27:重新定义你的Bulk_quote类,令其继承构造函数。
【出题思路】
本题练习继承构造函数的定义。
【解答】
#include <iostream>
#include <string>
#include <ostream>
using namespace std;
class Quote
{
public:
Quote() = default;
Quote(const std::string &book = "", double sales_price = 0.0)
:bookNo(book), price(sales_price)
{
cout << "Quote constructor is running===========" << endl;
}
string isbn() const
{
return bookNo;
}
//返回给定数量的书籍的销售总额,派生类改写并使用不同的折扣计算方法
virtual double net_price(std::size_t n) const
{
return n * price;
}
virtual void debug()
{
cout << "bookNo = " << bookNo << " price = " << price << endl;
}
virtual ~Quote()
{
cout << "Quote destructor is running=========" << endl;
}
friend ostream &operator << (ostream &, Quote &);
private:
std::string bookNo;//书籍的ISBN编号
protected:
double price = 0.0;//代表普通状态下不打折的价格
};
ostream& operator << (ostream &os, Quote &e)
{
os << "\tUsing operator << (ostram &, Quote &);" << endl;
return os;
}
class Disc_quote: public Quote
{
public:
Disc_quote(const string &book = "", double sales_price = 0.0, size_t qty = 0, double disc = 0.0)
:Quote(book, sales_price), quantity(qty), discount(disc)
{
}
double net_price(size_t cnt) const = 0;
protected:
size_t quantity;
double discount;
};
class Bulk_quote: public Disc_quote
{
public:
using Disc_quote::Disc_quote;
double net_price(size_t cnt) const
{
if(cnt > quantity)
return cnt * discount * price;
else
return cnt * price;
}
~Bulk_quote()
{
cout << "Bulk_quote destructor is running" << endl;
}
private:
size_t min_qty;
double discount;
};
ostream &operator << (ostream &os, Bulk_quote &bq)
{
os << "\tUsing operator << (ostram &, Bulk_quote &)" << endl;
return os;
}
int main(int argc, const char *argv[])
{
Quote base("C++ Primer", 128.0);
Bulk_quote bulk("Core Python Programming", 89, 5, 0.9);
cout << base << endl;
cout << bulk << endl;
return 0;
}
运行结果:
练习15.28:定义一个存放Quote对象的vector,将Bulk_quote对象传入其中。计算vector中所有元素总的net_price。
【出题思路】
本题是在容器中放置对象的使用练习。
【解答】
#include <iostream>
#include <string>
#include <ostream>
#include <vector>
using namespace std;
class Quote
{
public:
Quote() = default;
Quote(const std::string &book = "", double sales_price = 0.0)
:bookNo(book), price(sales_price)
{
cout << "Quote constructor is running========" << this << endl;
}
string isbn() const
{
return bookNo;
}
//返回给定数量的书籍的销售总额,派生类改写并使用不同的折扣计算方法
virtual double net_price(std::size_t n) const
{
return n * price;
}
virtual void debug()
{
cout << "bookNo = " << bookNo << " price = " << price << endl;
}
virtual ~Quote()
{
cout << "Quote destructor is running=========" << this << endl;
}
friend ostream &operator << (ostream &, Quote &);
private:
std::string bookNo;//书籍的ISBN编号
protected:
double price = 0.0;//代表普通状态下不打折的价格
};
ostream& operator << (ostream &os, Quote &e)
{
os << "\tUsing operator << (ostram &, Quote &);" << endl;
return os;
}
class Disc_quote: public Quote
{
public:
Disc_quote(const string &book = "", double sales_price = 0.0, size_t qty = 0, double disc = 0.0)
:Quote(book, sales_price), quantity(qty), discount(disc)
{
}
double net_price(size_t cnt) const = 0;
protected:
size_t quantity;
double discount;
};
class Bulk_quote: public Disc_quote
{
public:
using Disc_quote::Disc_quote;
double net_price(size_t cnt) const
{
if(cnt > quantity)
return cnt * discount * price;
else
return cnt * price;
}
~Bulk_quote()
{
cout << "Bulk_quote destructor is running " << this << endl;
}
private:
size_t min_qty;
double discount;
};
ostream &operator << (ostream &os, Bulk_quote &bq)
{
os << "\tUsing operator << (ostram &, Bulk_quote &)" << endl;
return os;
}
int main(int argc, const char *argv[])
{
vector<Quote> itemVec;
for(size_t i = 0; i != 5; ++i)
{
Bulk_quote item("C++ Primer", 6, 5, 0.5);
itemVec.push_back(item);
}
double sum = 0;
for(vector<Quote>::iterator iter = itemVec.begin(); iter != itemVec.end(); ++iter)
{
sum += iter->net_price(10);//调用Quote::net_price
}
cout << sum << endl;
return 0;
}
运行结果:
练习15.29:再运行一次你的程序,这次传入Quote对象的shared_ptr。如果这次计算出的总额与之前的程序不一致,解释为什么;如果一致,也请说明原因。
【出题思路】
本题是在容器中放置指针的使用练习,与放置对象的过程结果存在差异。
【解答】
p527和p537 在C++语言中,当我们使用基类的引用(或指针)调用一个虚函数时将发生动态绑定。普通对象调用不会发生动态绑定。
vector<shared_ptr<Quote>> itemVec;
程序产生的结果会存在差异。因为当通过Quote类型的对象调用虚函数net_price时,不实行动态绑定,调用的是Quote类中定义的版本;而通过Quote类型的指针调用虚函数net_price,实行动态绑定,而该指针实际指向Bulk_quote类中定义的版本。
练习15.30:编写你自己的Basket类,用它计算上一个练习中交易记录的总价格。
【出题思路】
本题是类构造编程练习。
【解答】
#include <iostream>
#include <string>
#include <ostream>
#include <vector>
#include <memory>
#include <set>//multiset
using namespace std;
class Quote
{
public:
Quote() = default;
Quote(const std::string &book = "", double sales_price = 0.0)
:bookNo(book), price(sales_price)
{
cout << "Quote constructor is running===========" << endl;
}
string isbn() const
{
return bookNo;
}
//返回给定数量的书籍的销售总额,派生类改写并使用不同的折扣计算方法
virtual double net_price(std::size_t n) const
{
return n * price;
}
virtual void debug()
{
cout << "bookNo = " << bookNo << " price = " << price << endl;
}
virtual ~Quote()
{
cout << "Quote destructor is running=========" << endl;
}
friend ostream &operator << (ostream &, Quote &);
private:
std::string bookNo;//书籍的ISBN编号
protected:
double price = 0.0;//代表普通状态下不打折的价格
};
ostream& operator << (ostream &os, Quote &e)
{
os << "\tUsing operator << (ostram &, Quote &);" << endl;
return os;
}
// calculate and print the price for the given number of copies, applying any discounts
double print_total(ostream &os, const Quote &item, size_t n)
{
// depending on the type of the object bound to the item parameter
// calls either Quote::net_price or Bulk_quote::net_price
double ret = item.net_price(n);
os << "ISBN: " << item.isbn() // calls Quote::isbn
<< " # sold: " << n << " total due: " << ret << endl;
return ret;
}
class Basket
{
public:
//Basket使用合成的默认构造函娄和拷贝制成员
void add_item(const std::shared_ptr<Quote> &sale)
{
items.insert(sale);
}
//打印每本书的总价和购物篮中所有书的总价
double total_receipt(std::ostream &) const;
private:
//该函数用于比较shared_ptr,multiset成员会用到它
static bool compare(const std::shared_ptr<Quote> &lhs, const std::shared_ptr<Quote> &rhs)
{
return lhs->isbn() < rhs->isbn();
}
//multiset保存多个报价,按照compare成员排序
std::multiset<std::shared_ptr<Quote>, decltype(compare) *> items{compare};
};
double Basket::total_receipt(ostream &os) const
{
double sum = 0.0;//保存实时计算出的总价格
//iter指向ISBN相同的一批元素中的第一个
//upper_bound返回一个迭代器,该迭代器指向这批元素的最后一个的下一位置
for(auto iter = items.cbegin(); iter != items.cend(); iter = items.upper_bound(*iter))
{
//我们知道在当前的Basket中至少有一个该关键字的元素
//打印该书籍对应的项目
sum += print_total(os, **iter, items.count(*iter));
}
os << "Total Sale: " << sum << endl;//打印最终的总价格
return sum;
}
int main(int argc, const char *argv[])
{
Basket sale;
sale.add_item(shared_ptr<Quote>(new Quote("123", 25)));
sale.add_item(shared_ptr<Quote>(new Quote("456", 35)));
sale.add_item(shared_ptr<Quote>(new Quote("789", 45)));
sale.total_receipt(cout);
return 0;
}
运行结果: