100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 一个泛型句柄类--C++模板和泛型编程--c++ primer

一个泛型句柄类--C++模板和泛型编程--c++ primer

时间:2020-01-10 00:20:43

相关推荐

一个泛型句柄类--C++模板和泛型编程--c++ primer

16.5. 一个泛型句柄类

这个例子体现了 C++ 相当复杂的语言应用,理解它需要很好地理解继承和模板。在熟悉了这些特性之后再研究这个例子也许会帮助。另一方面,这个例子还能很好地测试你对这些我的理解程序。

在第十五章定义了两个句柄类:Sales_item 类(第 15.8 节)和 Query 类(第 15.9 节)。这两个类管理继承层次中对象的指针,句柄的用户不必管理指向这些对象的指针,用户代码可以使用句柄类来编写。句柄能够动态分配和释放

相关继承类的对象,并且将所有“实际”工作转发给继承层次中的底层类。这两个句柄类似但并不相同:类似之处在于都定义了使用计数式的复制控制,管理指向继承层次中某类型对象的指针;不同之处在于它们提供给继承层次用户的接口。

两个类的使用计数的实现是相同的。这类问题非常适合于泛型编程:可以定义类模板管理指针和进行使用计数。原本不相关的 Sales_item 类型和 Query类型,可通过使用该模板进行公共的使用计数工作面得以简化。至于是公开还是

隐藏下层的继承层次,句柄可以保持不同。

本节将实现一个泛型句柄类(generic handle class),提供管理使用计数和基础对象的操作。然后,我们重新编写 Sales_item 类,展示它怎样使用泛型句柄而不是定义自己的使用计数操作。

16.5.1. 定义句柄类

Handle 类行为类似于指针:复制 Handle 对象将不会复制基础对象,复制之后,两个 Handle 对象将引用同一基础对象。要创建 Handle 对象,用户需要传递属于由 Handle 管理的类型(或从该类型派生的类型)的动态分配对象的地址,从此刻起,Handle 将“拥有”这个对象。而且,一旦不再有任意 Handle 对象与该对象关联,Handle 类将负责删除该对象。

对于这一设计,我们的泛型 Handle 类的实现如下:

template <class T> class Handle {public:// unbound handleHandle(T *p = 0): ptr(p), use(new size_t(1)) { }// overloaded operators to support pointer behaviorT& operator*();T* operator->();const T& operator*() const;const T* operator->() const;// copy control: normal pointer behavior, but last Handledeletes the objectHandle(const Handle& h): ptr(h.ptr), use(h.use){ ++*use; }Handle& operator=(const Handle&);~Handle() { rem_ref(); }private:T* ptr;// shared objectsize_t *use;// count of how many Handle spointto *ptrvoid rem_ref(){ if (--*use == 0) { delete ptr; delete use; } }};

这个类看来与其他句柄类似,赋值操作符也类似。

template <class T>inline Handle<T>& Handle<T>::operator=(const Handle &rhs){++*rhs.use;// protect against self-assignmentrem_ref(); // decrement use count and delete pointers ifneededptr = rhs.ptr;use = rhs.use;return *this;}

Handle 类将定义的其他成员是解引用操作符和成员访问操作符,这些操作符将用于访问基础对象。让这些操作检查 Handle 是否确实绑定到对象,可以提供一种安全措施。如果 Handle 没有绑定到对象,则试图访问对象将抛出一个异常。

这些操作的非 const 版本看来如下所示:

template <class T> inline T& Handle<T>::operator*(){if (ptr) return *ptr;throw std::runtime_error("dereference of unbound Handle");}template <class T> inline T* Handle<T>::operator->(){if (ptr) return ptr;throw std::runtime_error("access through unbound Handle");}

实现一个 Handle 类的自己的版本。

Exercises Section 16.5.1

Exercise

16.45:

实现一个 Handle 类的自己的版本。

Exercise

16.46:

解释复制 Handle 类型的对象时会发生什么。

Exercise

16.47:

Handle 类对用来实例化实际 Handle 类的类型有限制吗?如果有,限制有哪些?

Exercise

16.48:

解释如果用户将 Handle 对象与局部对象关联会发生什么。解释如果用户删除 Handle 对象所关联的对象会发生

什么。

16.5.2. 使用句柄

我们希望 Handle 类能够用于其他类的内部实现中。但是,为了帮助理解Handle 类怎样工作, 交首先介绍一个较简单的例子。 这个例子通过分配一个 int对象, 并将一个 Handle 对象绑定到新分配的 int 对象而说明 Handle 的行为:

{ // new scope// user allocates but must not delete the object to which the Handleis attachedHandle<int> hp(new int(42));{ // new scopeHandle<int> hp2 = hp; // copies pointer; use count incrementedcout << *hp << " " << *hp2 << endl; // prints 42 42*hp2 = 10; // changes value of shared underlying int} // hp2 goes out of scope; use count is decrementedcout << *hp << endl; // prints 10} // hp goes out of scope; its destructor deletes the int

即使是 Handle 的用户分配了 int 对象,Handle 析构函数也将删除它。在外层代码块末尾最后一个 Handle 对象超出作用域时,删除该 int 对象。为了访问基础对象,应用了 Handle 的 * 操作符,该操作符返回对基础 int 对象的引用。

使用 Handle 对象对指针进行使用计数作为在类实现中使用 Handle 的例子,可以重新实现 Sales_item 类(第15.8.1 节),该类的这个版本定义相同的接口,但可以通过用Handle<Item_base>:对象代替 Item_base 指针而删去复制控制成员:

class Sales_item {public:// default constructor: unbound handleSales_item(): h() { }// copy item and attach handle to the copySales_item(const Item_base &item): h(item.clone()) { }// no copy control members: synthesized versions work// member access operators: forward their work to the Handleclassconst Item_base& operator*() const { return *h; }const Item_base* operator->() const{ return h.operator->(); }private:Handle<Item_base> h; // use-counted handle};

虽然 Sales_item 类的接口没变,它的实现与原来的相当不同:

• 两个类都定义了默认构造函数和以 Item_base 对象为参数和 const 引用的构造函数。

• 两个类都将重载的 * 和 -> 操作符定义为 const 成员。

基于 Handle 的 Sales_item 版本有一个数据成员,该数据成员是关联传给构造函数的 Item_base 对象的副本上的 Handle 对象。因为 Sales_item 的这个版本没有指针成员,所以不需要复制控制成员,Sales_item 的这个版本可以

安全地使用合成的复制控制成员。管理使用计数和相关 Item_base 对象的工作在 Handle 内部完成。

因为接口没变, 所以不需要改变使用 Sales_item 类的代码。 例如, 第 15.8.3节中编写的程序可以无须改变而使用:

double Basket::total() const{double sum = 0.0; // holds the running totalfor (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 theBasket// virtual call to net_priceapplies appropriate discounts,if anysum += (*iter)->net_price(items.count(*iter));}return sum;}

调用 net_price 函数的语句值得仔细分析一下:

sum += (*iter)->net_price(items.count(*iter));

这个语句使用 -> 操作符获取并运行 net_price 函数, 重要的是理解这个操

作符怎样工作:

• (*iter) 返回 h,h 是使用计数式句柄的成员。

• 因此,(*iter)->使用句柄类的重载箭头操作符。

• 编译器计算h.operator->(),获得该 Handle 对象保存的 Item_base 指

针。

• 编译器对该 Item_base 指针解引用,并调用指针所指对象的 net_price

成员。

Exercises Section 16.5.2

Exercise

16.49:

实现本节提出的 Sales_item 句柄的版本,该版本使用泛型 Handle 类管理 Item_base 指针。

Exercise

16.50:

重新运行函数计算销售总额。列出让你的代码工作必须进行的所有修改。

Exercise

16.51:

重新编写 Section 15.9.4 第 15.9.4 节的 Query 类以使用泛型 Handle 类。注意你需要将 Handle 类设为

Query_base 类的友元,以使它能够访问 Query_base 构造函数。列出并解释让程序工作要做的其他所有修改。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。