15.8 容器和继承
在容器中管理继承体系的类时,如果需要多个子类都调用某一个操作,往往保留的是基类的指针,这做会更加的灵活。
练习
15.8 15.29
vector<shared_ptr<Quote>> vec;
/*vec.push_back(make_shared<Bulk_quote>("123", 20, 10, 0.9));
vec.push_back(make_shared<Bulk_quote>("123", 20, 10, 0.8));
vec.push_back(make_shared<Bulk_quote>("123", 20, 10, 0.8));*/
vec.push_back(make_shared<Quote>("123", 20));
vec.push_back(make_shared<Quote>("123", 20));
vec.push_back(make_shared<Quote>("123", 20));
double sum_money = 0.0;
for (const auto& item : vec)
{
sum_money = item->net_price(20);
}
cout << sum_money << endl;
在我编写的代码中结果是不一样的,因为Bulk_quote中设置了如果书本超过10本就打折,虽然vector保存的是Quote的指针类型,但是它调用的时net_price()函数,这个函数是虚函数,所以需要在运行时才能确定调用哪个动态类型定义的net_price()。在这里如果传入的是Bulk_quote,则调用net_price()调用的是Bulk_quote定义的。如果传入的是Quote,则调用Quote定义的net_price();
可以看到上面的代码可以对不同的类型的对象调用同一个操作,但是类的用户需要显式的传入智能指针。 在设计类时,应该尽可能的简化用户的操作。
因此可以设计一个辅助类。
15.30
按照书上写就ok,主要是要想明白设计思路。
只定义左值版本的add_item已经够用了,定义右值版本的add_item是为了提高性能。
有了右值版本的add_item() 就必须对右值类型做单独的操作,这里是定义了不同版本的clone(),一个clone()调用拷贝构造,一个clone()调用移动构造。
把clone定义在各自的类中,就可以通过基类的指针返回出不同的类型的对象,很巧妙
class Quote
{
public:
Quote()=default;
virtual ~Quote()=default;
Quote(const string& s, double p) :bookNo(s), price(p) {};
virtual double net_price(std::size_t n) const {
std::cout << "Quote::net_price" << std::endl;
return n * price;
}
virtual string debug() const ;
string isbn() const {
return bookNo;
};
virtual Quote* clone() const &{
//调用的是拷贝构造函数
return new Quote(*this);
}
virtual Quote* clone() const && {
//调用移动构造函数
return new Quote(std::move(*this));
}
private:
std::string bookNo;
protected:
double price = 0.0;
};
class Disc_quote:public Quote
{
public:
Disc_quote()=default;
Disc_quote(const string& book, double price, size_t qty, double disc) :Quote(book,price),discount_threshold(qty),discount(disc){};
virtual double net_price(size_t size)const=0;
protected:
size_t discount_threshold;
double discount;
private:
};
class Bulk_quote :
public Disc_quote
{
public:
Bulk_quote()=default;
~Bulk_quote() = default;
using Disc_quote::Disc_quote;
//Bulk_quote(const string& s, double p, size_t q, double dis);
double net_price(std::size_t n) const override;
virtual string debug() const override final;
//传入一个q是多次一举,因为本来就是克隆自身
/*Quote* clone(Quote* q) {
return new Quote(*q);
}*/
Quote* clone() const & override{
return new Bulk_quote(*this);
}
Quote* clone() const && override {
return new Bulk_quote(std::move(*this));
}
};
class Basket {
public:
//需要显式的传入指针
/*void add_item(const shared_ptr<Quote>& sale) {
item_set.insert(sale);
};*/
//隐式的传入,外部可能传入普通对象和new出来的对象
//最好的办法就是克隆一个对象,外部的内存由用户自己管理
void add_item(const Quote& sale) {
item_set.insert(shared_ptr<Quote>(sale.clone()));
}
void add_item(Quote&& sale){
item_set.insert(shared_ptr<Quote>(std::move(sale).clone()));
}
//但是在Basket中使用克隆,我们无法得知克隆的是Quote对象还是Bulk_quote对象
//所以想要clone出不同的类型,需要在各自的类中定义,而且需要定义为虚函数
//这样我们在Basket类中使用clone就不需要管动态类型
/*shared_ptr<Quote> clone() {
}*/
double total_prince(std::ostream&)const;
double print_single_price(std::ostream&,const Quote&,size_t ) const;
private:
static bool compare(const shared_ptr<Quote>&,const shared_ptr<Quote>&);
//这里传入的是函数指针,而不是函数类型
std::multiset<shared_ptr<Quote>, decltype(compare)*> item_set{compare};
};
double Bulk_quote::net_price(std::size_t n) const
{
std::cout << "Bulk_quote::net_price" <<std::endl;
double money=0.0;
if (n>= discount_threshold)
{
money = price * n*discount;
}
else {
money = n * price;
}
return money;
}
string Bulk_quote::debug() const
{
ostringstream str("");
str<<Quote::debug() << "threshold:" <<discount_threshold<<",Discount"<<discount;
return str.str();
}
Over_part_original_price::Over_part_original_price(const string & s, double p, size_t q, double dis):Disc_quote(s,p,q,dis)
{
}
double Over_part_original_price::net_price(std::size_t n) const
{
//std::cout << "Over_part_original_price::net_price(std::size_t n) " << std::endl;
double money = 0.0;
if (n<=discount_threshold)
{
money = n * price*discount;
}
else {
money = (n - discount_threshold)*price + discount_threshold * price*discount;
}
return money;
}
string Over_part_original_price::debug() const
{
ostringstream str("");
str<<Quote::debug()<< "threshodle" << this->discount_threshold << ",Discount" << discount;
return str.str();
}
double Disc_quote::net_price(size_t size) const
{
return 0.0;
}
double Basket::total_prince(std::ostream & output) const
{
double sum = 0.0;
for (auto iter = item_set.cbegin();iter!=item_set.cend();iter=item_set.upper_bound(*iter))
{
sum += print_single_price(output, **iter, item_set.count(*iter));
}
output << "sum:" << sum << endl;
return sum;
}
double Basket::print_single_price(std::ostream & out, const Quote & quote, size_t number) const
{
double money = quote.net_price(number);
out <<quote.isbn()<<" :"<< money<< endl;
return money;
}
bool Basket::compare(const shared_ptr<Quote>& q1, const shared_ptr<Quote>& q2)
{
return q1->isbn()<q2->isbn();
}