您好,欢迎访问代理记账网站
  • 价格透明
  • 信息保密
  • 进度掌控
  • 售后无忧

面向对象示例——Basekt类

目标

定义一个能存放继承关系的容器Baseket类

难点

容器中不能保存不同类型的元素,那么该如何将具有继承关系的多种类型的对象存放在容器中呢?比如Bulk_quote和Quote,因为我们不能将Quote转换为Bulk_quote,所以容器肯定不能存放Bulk_quote类型。那保存Quote类型吗?也不行,虽然保存Quote类型可以让Bulk_quote放置进该容器,但是会丢失派生类自己的部分。

vector<Quote> basket;
basket.push_back(Quote("0-201-82470-1", 50));
basket.push_back(Bulk_quote("0-201-54848-8", 50, 10, 0.25));
//正确的,但是只把对象的Quote部分拷贝给了basket
cout << basket.back().net_price(15) << endl;
//这里调用的是Quote版本的net_price

实际上当我们希望在容器中存放具有继承关系的对象时,我们存放的通常是基类的指针(智能指针更好)。这些指针所指对象的动态类型可能是基类类型,也可能是派生类类型

vector<shared_ptr<Quote>> basket;
basket.push_back(make_shared<Quote>("0-201-82470-1", 50));
basket.push_back(make_shared<Bulk_quote>("0-201-54848-8", 50, 10, 0.25));
//就像派生类的指针可以转换成基类指针
//这里派生类的智能指针转换成了基类的智能指针
//make_shared<Bulk_quote>返回指向Bulk_quote的智能指针
//调用push_back时将该智能指针转换成Quote的智能指针
cout << basket.back()->net_price(15) << endl;

实现

class Basket
{
public:
	void add_item(const shared_ptr<Quote> &sale) { items.insert(sale); }
	//打印每本书的总价和购物篮中所有书的总价
	double total_receipt(ostream&) const;
private:
	static bool compare(const shared_ptr<Quote> &lhs, const shared_ptr<Quote> &rhs)
	{ return lhs->isbn() < rhs->isbn(); }
	multiset<shared_ptr<Quote>, decltype(compare)*> items{compare};
	//multiset存放交易信息,可以保存同一本书的多条交易记录
	//shared_ptr没有定义小于运算符,所以定义上面的compare
};

可能multiset不太容易理解,从左向右读实际就是一个指向Quote对象的shared_ptr的multiset,这个multiset使用一个和compare成员类型相同的函数来对其中的元素进行排序。multiset的名字是items,我们初始化items并令其使用我们的compare函数

定义Basket成员

total_receipt

total_receipt函数负责将购物篮的内容逐项打印成清单,最后返回总价格

double Basket::total_receipt(ostream &os) const
{
	double sum = 0.0;
	for (auto iter = items.cbegin(); iter != items.end(); iter = items.upper_bound(*iter))
	//upper_bound返回一个迭代器,该迭代器指向所有和iter关键字相等的元素的后一个元素的像一个位置
		sum += print_total(os, **iter, items.count(*iter));
		//*iter是指向准备打印对象的指针,**iter是准备打印的对象,可能是Quote也可能是派生类
		//count是multiset的函数,用来统计multiset有多少元素键值相同
	os << "Total Sale: " << sum << endl;
	return sum;
}

add_item

现在的add_item接受一个shared_ptr参数,也就是说购物篮的用户必须自己处理动态内存。我们应该重新定义add_item,接受一个Quote对象而不是shared_ptr。新的add_item负责处理内存分配,这样用户就会省事了

//定义两个,一个拷贝,一个移动
void add_item(const Quote& sale);
void add_item(Quote&& sale);

还有个问题是add_item不知道要分配什么类型。进行内存分配时会拷贝(移动)sale参数,所以肯定会有这样的表达式new Quote(sale)。这里是分配Quote 类型的对象,然后拷贝sale的Quote部分,但sale也能是Bulk_quote对象,这样的话派生类部分又被切掉了…

模拟虚拷贝

为了解决上面的问题,我们给Quote添加一个虚函数,这个函数返回当前对象的动态内存

class Quote
{
public:
	//该虚函数返回当前对象的一份动态分配的拷贝
	virtual Quote* clone() const & {return new Quote(*this);}
	//const左值引用成员将自己拷贝到新分配的对象
	virtual Quote* clone() && {return new Quote(std::move(*this));}
	//右值引用成员将自己移动到新数据
};
class Bulk_quote
{
	Bulk_quote* clone() const & {return new Bulk_quote(*this);}
	Bulk_quote* clone() && {return new Bulk_quote(std::move(*this));}
};

然后重新写add_item

class Basket
{
public:
	void add_item(const Quote& sale)
		{ items.insert(shared_ptr<Quote>(sale.clone())); }
	void add_item(Quote&& sale)
		{ items.insert(shared_ptr<Quote>(std::move(sale).clone()));}
	//这样动态类型就能决定返回谁的动态内存
};

分享:

低价透明

统一报价,无隐形消费

金牌服务

一对一专属顾问7*24小时金牌服务

信息保密

个人信息安全有保障

售后无忧

服务出问题客服经理全程跟进