C++语言99个常见编程错误 常见错误90:非适当的容器类型值可替换性

常见错误90:非适当的容器类型值可替换性
  对C++软件工程师而言,STL容器组件应该是容器应用的首先。不过,STL容器组件并不能满足
所有的需求,因为它们为了发挥出强大的功能,不得不设置一定的限制。有关STL容器组件
的一个有意思的事实是:由于它们都是以模板实现的,其结构和行为诸多方面都是编译时就决定
了的。这种特点带来了短小精悍的实现,在编译时精确地转化成恰好合用的静态执行码。
  但是,并不一定在编译时就能准备好所有的有关信息。
  在设计中使用平行继承谱系通常来讲是有问题的,因为对其中一个继承谱系的修改会要求
其他的继承谱系作出对应的变化。我们希望能够将代码更改集中一处。
  工厂方式设计模式为抽象基类接口的用户提供了一种机制,使他们能够生成适当派生类对象,
同时又能对其具体类型保持不知情。
  通常,工厂方法设计模式是病态的基于类型分派的代码的替代方案。
  有一个不幸但又是普遍存在的趋势,就是看到容量持有元素类型的可替代性就想当然地
认为持有这些元素的容器类型亦有可替换性。可替换性在数组这个C++语言内建的容器上
是没有的。持有派生类对象的数组对于持有基类对象的数组并无可替换性。警示着用户
自定义的持有可替换类型元素的容器类型不能如此行事。
  容器类型之间的可替代性,如果需要的话,那么设计时应该关注其本身的结构,而非其

持有元素类型的结构。


bondlist.h
#ifndef BONDLIST_H
#define BONDLIST_H

class Object
    { public: virtual ~Object(){} };
class Instrument : public Object
    { public: virtual double pv() const = 0; };
class Bond : public Instrument
    { public: double pv() const; };
class ObjectList {
  public:
  	ObjectList() : c(0) {}
    void insert( Object *o ) { a[c++] = o; }
    Object *get();
    size_t size() const { return c; }
    Object *operator [](int i) { return a[i]; }
    //...
    private:
    int c;
    Object *a[100];
};
class BondList : public ObjectList { // bad idea!!!
  public:
    void insert( Bond *b )
        { ObjectList::insert( b ); }
    Bond *get()
        { return static_cast<Bond *>(ObjectList::get()); }
    Bond *operator []( int a ) { return (Bond *)ObjectList::operator [](a); }
    //...
};

#endif

bondlist.cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include "bondlist.h"

double bondPortfolioPV( BondList &bonds ) {
	double sumpv = 0.0;
	for( int i = 0; i < bonds.size(); ++i ) {
		Bond *b = bonds[i];
		sumpv += b->pv();
	}
	return sumpv;
}
class UnderpaidMinion : public Object {
  public:
    virtual double pay()
        { std::cout << "UnderpaidMinion::pay!" << std::endl; return 0.0; }
};
void sneaky( ObjectList &list )
    { list.insert( new UnderpaidMinion ); }
    
BondList bondList;
BondList &getBondList() {
	return bondList;
}

void victimize() {
    BondList &blist = getBondList();
    sneaky( blist );
    bondPortfolioPV( blist ); //done!
}


container.h
#ifndef CONTAINER_H
#define CONTAINER_H

template <typename T>
class Iter;

template <typename T>
class Container {
  public:
    virtual ~Container() {}
    virtual Iter<T> *genIter() const = 0; // Factory Method
    virtual Container *clone() const = 0; // Prototype
    virtual void insert( const T & ) = 0;
};

template <typename T>
class Iter {
  public:
    virtual ~Iter() {}
    virtual void reset() = 0;
    virtual void next() = 0;
    virtual bool done() const = 0;
    virtual T &get() const = 0;
};

#endif

list.h
#ifndef LIST_H
#define LIST_H

#include <memory>

template <typename T>
class ListIter;

template <typename T>
struct ListNode {
  	ListNode( ListNode *n, const T &t )
  		: n_( n ), e_( t ) {}
  	ListNode *n_;
  	T e_;
};

template <typename T>
class List : public Container<T> {
  public:
  	List()
  		: hd_( 0 ) {}
    ~List();
    ListIter<T> *genIter() const
    	{ return new ListIter<T>( *this ); }
    List *clone() const;
    void insert( const T &t )
    	{ ListNode<T> *tmp = new ListNode<T>( hd_, t ); hd_ = tmp; }
  private:
	friend class ListIter<T>;
	List( const List & );
	List &operator =( const List & );
  	ListNode<T> *hd_;
};

template <typename T>
class ListIter : public Iter<T> {
  public:
  	ListIter( const List<T> &l )
  		: c_( l.hd_ ), l_( &l ) {}
    virtual ~ListIter() {}
    virtual void reset()
    	{ c_ = l_->hd_; }
    virtual void next()
    	{ c_ = c_->n_; }
    virtual bool done() const
    	{ return !c_; }
    virtual T &get() const
    	{ return c_->e_; }
  private:
  	ListNode<T> *c_;
  	const List<T> *l_;
};

template <typename T>
List<T>::~List() {
	ListNode<T> *p = hd_, *q;
	while( p ) {
		q = p;
		p = p->n_;
		delete q;
	}
}

template <typename T>
List<T> *List<T>::clone() const {
	// not efficient, but interesting...
	std::auto_ptr<List> n1( new List );
	for( ListIter<T> i( *this ); !i.done(); i.next() )
		n1->insert( i.get() );
	List *n2( new List );
	for( ListIter<T> i( *n1 ); !i.done(); i.next() )
		n2->insert( i.get() );
	return n2;
}
    	
#endif


main.cpp
#include <memory>
#include <string>
#include <iostream>
#include "container.h"
#include "list.h"

template <typename T>
void print( Container<T> &c ) {
    std::auto_ptr< Iter<T> > i( c.genIter() );
    for( i->reset(); !i->done(); i->next() )
        std::cout << i->get() << std::endl;
}

int main() {
	std::auto_ptr< List<int> > c1( new List<int> );
	for( int i = 0; i < 10; ++i )
		c1->insert( i );
	print( *c1 );
	
	std::auto_ptr< Container<std::string> > c2( new List<std::string> );
	c2->insert( "Andrei" );
	c2->insert( "Dan" );
	c2->insert( "Herb" );
	c2->insert( "Scott" );
	print( *c2 );
	
	std::auto_ptr< Container<std::string> > c3( c2->clone() );
	print( *c3 );
	
	extern void victimize();
	victimize();
	getchar();
	return 0;
}


输出
9
8
7
6
5
4
3
2
1
0
Scott
Herb
Dan
Andrei
Scott
Herb
Dan
Andrei
UnderpaidMinion::pay!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值