复合关系
当某种类型的对象内含其他种类型的对象时,它们之间便是这种复合关系。比如:
class Address { //... };
class PhoneNumber { //... };
class Person
{
public:
//...
private:
std::string name;
Address address;
PhoneNumber voiceNumber;
PhoneNumber faxNumber;
};
在这里例子中,Person 对象就由 std::string, Address, PhoneNumber 复合而成。
has-a 以及 is-implemented-in-tems-of
之前说过,public 继承就意味着 is-a 的关系。对于复合,它也有它对应的含义,它有两个意义。复合意味着 has-a 或者 is-implemented-in-terms-of。
上面的 Person class 就是 has-a 的关系。比较麻烦的是区分 is-a 和 is-implemented-in-terms-of 的关系。
比如说,你需要写出一个模板类,来实现由不重复对象组成的容器。你的第一反应,当时是直接使用 stl 中提供的 std::set 容器。但是这个容器背后是由红黑树实现的,虽然查找、插入、删除的速度都在对数级,但是每个元素都有三个指针的额外开销。你不希望用空间换时间,所以想自己另外实现一个模板类。
经过考虑之后,你决定顶层用 stl 中的 std::list 去实现 std::set 的功能。要想调用 std::list 。当然有两种实现形式:第一是继承它,这样就可以获得它的成员;或者是内含它的对象或指针,通过这个对象或指针去调用它的成员。如果你打算采用第一种方式:
template<typename T>
class MySet : public std::list<T> { //... }
看起来很美好,其实不是。之前也反复说过,public 继承意味着 is-a 的关系,对于基类来说任何有意义的行为对于子类来说也都需要有意义。但是在这里,std::list 是可以拥有相同的数据的,而 MySet 不行。所以,MySet 是一种 std::list 并不为真,因为某些对于 std::list 为真的东西对于 MySet 而言并不为真。
正确的做法,是让 MySet 内含一个 std::list 对象:
template <typename T>
class MySet
{
public:
bool member(const T& item) const;
void insert(const T& item);
void remove(const T& item);
std::size_t size() const;
private:
std::list<T> rep;
};
template<typename T>
bool MySet<T>::member(const T& item) const
{
return std::find(rep.begin(), rep.end(), item) != rep.end();
}
template<typename T>
void MySet<T>::insert(const T& item)
{
if(!member(item))
rep.push_back(item);
}
template<typename T>
void MySet<T>::remove(const T& item)
{
typename std::list<T>::iterator it = std::find(rep.begin(), rep.end(), item);
if(it != rep.end())
rep.erase(it);
}
template<typename T>
std::size_t MySet<T>::size() const
{
return rep.size();
}
这里的 MySet 和 std::list 就是 is-implemented-in-terms-of 的关系。
本文探讨了C++中的复合关系,特别是has-a和is-implemented-in-terms-of的概念。通过示例解释了Person类如何展示has-a关系,并分析了使用std::list实现std::set功能时,应当避免错误的public继承,而应选择is-implemented-in-terms-of的关系来正确实现。
949

被折叠的 条评论
为什么被折叠?



