25、C++ Primer 4th 笔记,面向对象编程(3)

1、在继承情况下,派生类的作用域嵌套在基类作用域中。正是这种类作用域的层次嵌套使我们能够直接访问基类的成员。

2、与基类成员同名的派生类成员将屏蔽对基类成员的访问。可以使用作用域操作符访问被屏蔽的基类成员。

3在基类和派生类中使用同一名字的成员函数,其行为与数据成员一样:在派生类作用域中派生类成员将屏蔽基类成员。即使函数原型不同,基类成员也会被屏蔽。

4、通过派生类对象调用基类对象时,实参必须与派生类中定义的版本相匹配,只有在派生类根本没有定义该函数时,才考虑基类函数。

如果派生类想通过自身类型使用所有的重载版本,则派生类必须要么重定义所有的版本,要么一个也不重定义。

可以通过using为基类成员函数名称进行声明,将所有该函数的重载实例都加到派生类的作用域中。

5、名字查找与继承

1首先确定进行函数调用的对象、引用或指针的静态类型。

2)在该类中查找函数,如果找不到,就在直接基类中查找,如此循着类的继承链往上找,直到找到该函数或者查找完最后一个类。

    如果不能在类或其相关基类中找到该名字,则调用是错误的。

3)一旦找到了该名字,就进行常规类型检查,通过查看找到的定义,来检查该函数调用是否合法。

4)假定函数调用合法,编译器就生成代码如果函数是虚函数且通过引用或指针调用,则编译器生成代码以确定根据对象的动态类型运行哪个函数版本,否则,编译器生成代码直接调用函数。

6、含有(或继承)一个或多个纯虚函数的类是抽象基类。除了作为抽象基类的派生类的对象的一部分,不能创建抽象类型的对象。

7、容器与继承

在容器中保存有继承关系的对象,如果定义成保存基类对象,则派生类将被切割,如果定义成保存派生类对象,则保存基类对象又成问题(基类对象将被强制转换成派生类对象,而派生类中定义的成员未被初始化)。

唯一的可行的选择是容器中保存对象的指针。但是需要用户管理对象和指针。C++中一个通用的技术是包装类(cover)或句柄类(handle)。用句柄类存储和管理类指针。

8、包装了继承层次的句柄有两个重要的设计考虑因素:

1)像对任何保存指针的类一样,必须确定对复制控件做些什么。包装了继承层次的句柄通常表现得像一个智能指针或者像一个值。

2)名柄类决定句柄接口屏蔽还是不屏蔽继承层次,如果不屏蔽继承层次,用户必须了解和使用基本层次中的对象(objects in the underlying hierarchy)

    句柄类经常需要在不知道对象的类型时分配已知对象的副本。解决这个问题的通用方法是定义虚操作进行复制,称该操作为clone,如示例中。

句柄类示例

#ifndef BASKET_H
#define BASKET_H

#include <iostream>
#include <string>
#include <set>
#include <map>
#include <utility>
#include <cstddef>

// Item sold at an undiscounted price
// derived classes will define various discount strategies
class Item_base {
	friend std::istream& operator>>(std::istream&, Item_base&);
	friend std::ostream& operator<<(std::ostream&, const Item_base&);
public:
	virtual Item_base* clone() const 
	{
		return new Item_base(*this); 
	}
public:
	Item_base(const std::string &book = "", double sales_price = 0.0):
	          isbn(book), price(sales_price) { }

	std::string book() const { return isbn; }

	// returns total sales price for a specified number of items
	// derived classes will override and apply different discount algorithms
	virtual double net_price(std::size_t n) const 
	{ 
		return n * price; 
	}

	// no work, but virtual destructor needed 
	// if base pointer that points to a derived object is ever deleted
	virtual ~Item_base() { } 
private:
	std::string isbn;   // identifier for the item
protected:
	double price;       // normal, undiscounted price
};

// class to hold discount rate and quantity 
// derived classes will implement pricing strategies using these data
class Disc_item : public Item_base
{
public:
	std::pair<size_t, double> discount_policy() const
	{ 
		return std::make_pair(quantity, discount); 
	}
	// other members as before
	double net_price(std::size_t) const = 0; //声明为纯虚函数
	Disc_item(const std::string& book = "", double sales_price = 0.0, std::size_t qty = 0, double disc_rate = 0.0):
					Item_base(book, sales_price), quantity(qty), discount(disc_rate) { }
protected:
	std::size_t quantity;  // purchase size for discount to apply
	double discount;       // fractional discount to apply
};

// discount kicks in when a specified number of copies of same book are sold
// the discount is expressed as a fraction to use to reduce the normal price
class Bulk_item : public Disc_item {
public:
	std::pair<size_t, double> discount_policy() const
	{ 
		return std::make_pair(quantity, discount); 
	}
	// other members as before
	Bulk_item* clone() const 
	{ 
		return new Bulk_item(*this); 
	}
	Bulk_item(const std::string& book = "", double sales_price = 0.0, std::size_t qty = 0, double disc_rate = 0.0):
				Disc_item(book, sales_price, qty, disc_rate) { }

	// redefines base version so as to implement bulk purchase discount policy
	double net_price(std::size_t) const;
};

// discount (a fraction off list) for only a specified number of copies, 
// additional copies sold at standard price
class Lim_item : public Disc_item {
public:
	Lim_item(const std::string& book = "", double sales_price = 0.0, std::size_t qty = 0, double disc_rate = 0.0):
	Disc_item(book, sales_price, qty, disc_rate) { }

	// redefines base version so as to implement limited discount policy
	double net_price(std::size_t) const;
	Lim_item* clone() const 
	{ 
		return new Lim_item(*this); 
	}
	std::pair<size_t, double> discount_policy() const
	{ 
		return std::make_pair(quantity, discount); 
	}
};

// use counted handle class for the Item_base hierarchy,句柄类
class Sales_item {
	// let compare use the Item_base pointer to use Item_base compare function

	// this friend is needed for the hidden call to print_total, 
	friend class Basket;   
public:
	// default constructor: unbound handle
	Sales_item(): p(0), use(new std::size_t(1)) { }
	// attaches a handle to a copy of the Item_base object
	Sales_item(const Item_base&); 

	// copy control members to manage the use count and pointers
	Sales_item(const Sales_item &i): p(i.p), use(i.use) 
	{
		++*use; 
	}
	~Sales_item()
	{ 
		decr_use(); 
	}
	Sales_item& operator=(const Sales_item&);

	// member access operators
	const Item_base *operator->() const 
	{ 
		return p; 
	}
	const Item_base &operator*() const 
	{ 
		return *p; 
	}
private:
	Item_base *p;         // pointer to shared item
	std::size_t *use;     // pointer to shared use count

	// called by both destructor and assignment operator to free pointers
	void decr_use() 
	{ 
		if (--*use == 0) 
		   {
			   delete p; 
			   delete use;
		   } 
	}
};

bool compare(const Sales_item &, const Sales_item &);
// holds items being purchased
class Basket {
	// type of the comparison function used to order the multiset
	typedef bool (*Comp)(const Sales_item&, const Sales_item&);
	std::multiset<Sales_item, Comp> items;
public:
	// useful typedefs modeled after corresponding container types
	typedef std::multiset<Sales_item, Comp>::size_type size_type;
	typedef std::multiset<Sales_item, Comp>::const_iterator const_iter;

	// workaround MS compiler bug: must explicitly pass function address
	Basket(): items(&compare) { }  // initialze the comparator
	void display(std::ostream&) const;

	void add_item(const Sales_item &item) 
	{ 
		items.insert(item); 
	}
	size_type size(const Sales_item &i) const
	{ 
		return items.count(i); 
	}
	double total() const;  // sum of net prices for all items in the basket
};

inline
Sales_item::Sales_item(const Item_base &item):p(item.clone()), use(new std::size_t(1)) { }

// compare defines item ordering for the multiset in Basket
inline bool 
compare(const Sales_item &lhs, const Sales_item &rhs) 
{
	return lhs->book() < rhs->book();
} 

#endif

#include <algorithm>
using std::multiset; using std::map; using std::pair; using std::size_t;
using std::string; using std::ostream; using std::endl; using std::min;
using std::cout;

// debugging routine to check contents in a Basket
void Basket::display(ostream &os) const
{
	os << "Basket size: " << items.size() << endl;

	// print each distinct isbn in the Basket along with
	// count of how many copies are ordered and what their price will be
	// upper_bound returns an iterator to the next item in the set
	for (const_iter next_item = items.begin();
		next_item != items.end();
		next_item = items.upper_bound(*next_item))
	{
		// we know there's at least one element with this key in the Basket
		os << (*next_item)->book() << " occurs " 
			<< items.count(*next_item) << " times" 
			<< " for a price of " 
			<< (*next_item)->net_price(items.count(*next_item)) 
			<< endl;
	}
}

void print_total(ostream &, const Item_base&, size_t);

// calculate and print price for given number of copies, applying any discounts 
void print_total(ostream &os, 
				 const Item_base &item, size_t n)
{
	os << "ISBN: " << item.book() // calls Item_base::book
		<< "\tnumber sold: " << n << "\ttotal price: "
		// virtual call: which version of net_price to call is resolved at run time
		<< item.net_price(n) << endl;
}

double Basket::total() const
{
	double sum = 0.0;    // holds the running total 


	for (const_iter iter = items.begin(); 
		iter != items.end();
		iter = items.upper_bound(*iter))
	{
		// we know there's at least one element with this key in the Basket
		print_total(cout, *(iter->p), items.count(*iter));
		// virtual call to net_price applies appropriate discounts, if any
		sum += (*iter)->net_price(items.count(*iter));
	}
	return sum;
}

// use-counted assignment operator; use is a pointer to a shared use count
Sales_item&
Sales_item::operator=(const Sales_item &rhs)
{
	++*rhs.use;
	decr_use();
	p = rhs.p;
	use = rhs.use;
	return *this;
}

// if specified number of items are purchased, use discounted price 
double Bulk_item::net_price(size_t cnt) const
{
	if (cnt >= quantity)
		return cnt * (1 - discount) * price;
	else
		return cnt * price;
}

// use discounted price for up to a specified number of items
// additional items priced at normal, undiscounted price
double Lim_item::net_price(size_t cnt) const
{
	size_t discounted = min(cnt, quantity);
	size_t undiscounted = cnt - discounted;
	return discounted * (1 - discount) * price 
		+ undiscounted * price;
}

int main()
{
	//...
	return 1;
}

句柄类示例

9、虚函数的重写中,派生类的返回类型必须与基类实例的返回类型完全匹配,但有一个例外:如果虚函数的基类实例返回类类型的引用或指针,则该虚函数的派生类实例可以返回基类实例返回的类型的派生类对象(或者是类类型的指针或引用)。

10、在定义关系运算符时,如定义==,<=时,其逻辑要一致。

参考

[1] multiset使用

http://blog.163.com/zhoumhan_0351/blog/static/399542272010396030501/

上一篇:有状态的 web 应用


下一篇:软件设计精要与模式学习笔记--张逸