条款38:通过复合塑膜出has-a或"根据某物实现"
内容:
所谓的复合就是类型之间的一种关系,它的基本的表现形式在我们平时的编写代码过程当中常常出现,比如你准备设计一个类的时候,你写出来的类的对象不是保护了其它的对象?呵呵,对了,这就叫复合.举个例子:
class Address{...};
class PhoneNumber{...};
class Person{
...
private:
std::string name_;
Address address_;
PhoneNumber voiceNumber_;
PhoneNumber faxNumber_;
};
本例中Person就包含了string,Address,PhoneNum合成成分物,它们之间就构成了复合的关系,是不是很简单?呵呵!在前面的条款32中我们提到public继承体系下的子类与父类之间带有"is-a"的意义,而在这里,复合也有自己的意义,它有两个实际的意义,复合意味着"has-a"(有一个)或者is-implemented-in-term-of(根据某物实现),前者的对象属于应用域部分,相当于你塑造的世界中的某些事物,例如人,汽车等.后者的对象则是实现细节人工产品(这产品现实世界中是没有的),像什么mutex,list,container等等,这些对象是你的软件的实现领域.这两个意义理解起来并不是很困难,但在实际的编写代码中如何区分这两种对象关系却并不是你想的那么容易.
现在的你希望有一个template class去表现一组不重复的对象组成的set(集合),当然你首先想到的就是STL中的set容器,我们都知道STL中set容器是通过balanced search trees实现而成的,它在查找、安插、移除元素时保证较好的速度效率,但却占用了足够客观的空间,如果你的程序的空间比速度要重要,那么标准库的set就不是我们想要的东西了,那怎么办?很简单嘛,你自己写一个不就得了嘛,呵呵.
考虑到代码的复用,我就想尽量能用现成的代码来模拟它,要是自己亲自每一步都来写,得花我不少时间呢?呵呵,没办法,我这人就是比较懒,想来想去,我决定底层用linked list来实现,因为标准库中有list容器!可以把set对象看成list对象,于是我就这样声明了set template:
template<typename T>
class Set:public std::list<T>{...}
看起来似乎不错,不过稍微细想了一下,觉得有些地方不得劲:如果Set是list子类的话,那么list里面的每一件事情对Set来说应该都适用,但list可以含重复元素Set却不可以,晕死!所以说将这两个类强扭成父子关系并不是很合适,其实在这里你的Set对象可以由一个list塑膜出来,你可以这样做:
template <class T>
class Set{
public:
bool member(const T& item)const{
return std::find( list_.begin(), list_.end(), item ) != list_.end() );
}
void insert(const T& item){
if( !member(item) )
list_.push_back(item);
}
void remove(const T& item){
typename std::list<T>::iterator iter = std::find( list_.begin(),
list_.end(),
item );
if( iter != list_.end() )
list_.erase(iter);
}
size_t size()const{
return list_.size();
}
private:
std::list<T> list_;
};
今天的内容比较少,也比较简单,但是在平时的编写代码的时候我们却经常遇到,所以大家要注意.
请记住:
■ 复合的意义和public继承完全不同.
■ 在应用域,复合意味着has-a(有一个).在实现域,复合意味着is-implemented-in-terms-of(根据某物实现出).