C++ Primer 第十五章 面向对象程序设计 15.8 容器和继承 练习和总结

本文探讨了在C++中如何在容器中处理继承体系,特别是使用基类指针来管理多种子类的情况。通过分析15.8章节的练习15.29和15.30,解释了如何利用虚函数实现动态类型绑定,并讨论了为提高性能而设计的右值版本add_item()和不同版本的clone()方法。内容强调简化用户操作和灵活处理不同类型对象的重要性。
摘要由CSDN通过智能技术生成

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();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值