对象rep,并且Plist的每个成员函数调用rep的相应的List函数:- Template<class T>
- Class Plist {
- Public:
- T* head() const {return (T*)rep.head(); }
- Void insert(T* t) {rep.insert(t); }
- Private:
- List<void*> rep;
- //...
- };
如果可以成功地把T*类型的值转化为void*类型的值,并且可以转化回来,从而得到链表节点的原始值,那么上面的代码就可以实现我们希望的功能。虽然有些T类型并没有上面的转化特性(例如,指向成员的指针类型),但普通用户所希望插入容器的类型一般都具有上述转化特性(关于Plist究竟只适用于哪些类型,应该提供清楚的文档资料)。
为了实现tail函数,我们使用了Plist的私有构造函数:
- template<class T>
- class Plist {
- private:
- Plist (const List<void*>& r) : rep(r) { }
- //...
- };
- template<class T>
- Plist<T> Plist<T>::tail() const {
- Return Plist<T>(rep.tail());
- }
从上面可以看出,Plist成员函数的代码实体是很简单的;实际上,这些函数都是内联函数的候选函数,而且内联(我们在4.2.2节进行讨论)通常是实例化函数最快的方式;另外,我们还可以预实例化List<void*>对象。因此,比起那些使用List<Widget*>和List<Biidget*>的程序员,使用Plist<Widget>和Plist<Blidget>的程序员只需要实例化更少的代码,而且Plist的用户将看到程序创建时间也相应减少了。
指针容器虽然减少了实例化时间,但却有一个显著的缺点:容器类和指针容器,虽然从概念上讲,在C++(www.cppentry.com)的类型系统中应该是关联的,但实际上C++(www.cppentry.com)类型系统并没有实现这种关联。假设List和Plist的用户编写了下列代码:
- template<class T>
- void insert_twice(const T& t, List<T>& 1) {
- 1.insert(t);
- 1.insert(t);
- }
虽然对于任何类型的List,我们都可以调用这个函数,但这个函数却不能使用Plist类型为参数。为了提供和Plist类型相同的功能,我们需要另一个函数:- template<class T>
- Void insert_twice(const T& t, Plist<T>& 1) {
- 1.insert(t);
- 1.insert(t);
- }
某些程序库设计者曾经尝试过解决这个问题,他们通过继承把一个类和这个类的指针容器相关在一起: - //这种使用继承的设计并不是一种好的设计
- template<class T>
- class Plist : public List<void*> {
- public:
- T* head() const {return (T*)Lisyt<void*>::head();}
- //...
- };
然而遗憾的是,这个设计并不是类型安全的。如考虑下面用户的代码,其中X和Y是不相关的类型:
- void insert_y(List<void*>& 1) {
- 1.insert(new Y);
- }
- int main() {
- plist<X> l;
- insert_y(1); //在List l中插入一个类型为Y*的值
- x* x = 1.head; //出问题了,我们得到一个x*类型的值
- //...
- }
上面的代码既没有编译时错误,又没有创建时错误;然而,它把一个Y*类型的值转化为一个X*类型的值,而这并不是一个安全的转型。