抽象数据类型之容器(一)

来源:互联网 发布:gre知乎 编辑:程序博客网 时间:2024/06/04 07:29

        本系列文章基于我之前的“抽象数据类型与类层次”系列,专门讲解“抽象容器”及其相关概念。(点击打开链接)


一、容器类    

        根据上面的类层次图,容器也是一种“Object”。容器是一种能够存储其他物体的物体。下面使用一个容器类来抽象容器这个概念。

<span style="font-size:14px;">#pragma once#include "Object.h"#include "OwnerShip.h"#include "Visitor.h"#include "Iterator.h"class Container : public virtual Object, public virtual OwnerShip{public:    virtual unsigned int Count() const;    virtual bool IsEmpty() const;    virtual bool IsFull() const;    //virtual std::hash<Object> Hash() const;    virtual void Put(std::ostream &)const;    virtual Iterator & NewIterator() const;    virtual void Purge() = 0;    virtual void Accept(Visitor &) const = 0;protected:    Container();    unsigned int count;};</span>

注:容器类是一种抽象类,并不产生实际的具体对象,故其构造函数可以是“Protercted”。

<span style="font-size:14px;">#include "stdafx.h"#include "Container.h"#include "NullIterator.h"#include <typeinfo>Container::Container()    : count(0){}unsigned int Container::Count() const{    return count;}bool Container::IsEmpty() const{    return Count() == 0;}bool Container::IsFull() const{    return false;}void Container::Put(std::ostream & s) const{    PuttingVisitor visitor(s);    s << typeid(*this).name() << " {";    Accept(visitor);    s << "}";}Iterator & Container::NewIterator() const{    return *new NullIterator();}</span>

注:“IsEmpty函数”中使用的不是成员变量来直接判断,是因为考虑到容器的子类对计数或空容器的判断方法可能重载。


二、Visitor类

        在上面容器类中,有一个“Accept接口函数”,该函数需要一个Vistitor类引用作为参数。这个接口用到的就是Visitor Pattern(访问者模式)。

<span style="font-size:14px;">class Visitor{public:    virtual void Visit(Object &) = 0;    virtual bool IsDone() const { return false; }};</span>

        Visitor类其实非常简单,它只有两个接口函数,其中“Visit函数”接受一个Object引用为参数,对该Object对象进行某些处理。因此,Visitor与Container有一个交互,这个交互过程是这样的:Container通过调用Accept函数接受一个Visitor,即接受一种算法或处理方式,然后遍历Container的各个元素,将元素逐个传递给Visitor,对他们逐个进行Visit操作(算法)。这其实也是一种将算法与待处理对象分离的设计方法。

<span style="font-size:14px;">void SomeContainer::Accept (Visitor& visitor) const    for each Object i in this container        if (visitor.IsDone ())            return;        visitor.Visit (i);</span>

<span style="font-size:14px;"><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">        Visitor类的另一个函数“IsDone”可以作为“Accept函数”中的循环中断条件。</span></span>
<span style="font-size:14px;"><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">        下面再展示几个具体的Visitor类和全部代码:</span></span>

<span style="font-size:14px;">#ifndef VISITOR_H#define VISITOR_H#include "Object.h"#include <ostream>class Visitor{public:    virtual void Visit(Object &) = 0;    virtual bool IsDone() const { return false; }};class PuttingVisitor : public Visitor{public:    PuttingVisitor(std::ostream & s)        : stream(s), comma(false)    {}    void Visit(Object & object)    {        if (comma)            stream << ", ";        stream << object;        comma = true;    }private:    std::ostream & stream;    bool comma;};class MatchingVisitor : public Visitor{public:    MatchingVisitor(Object const & object)        : target(object), found(NULL)    {}    void Visit(Object & object)    {        if (found == NULL && object == target)            found = &object;    }    bool IsDone() const { return found != NULL; }private:    Object const & target;    Object * found;};#endif // VISITOR_H</span>

        返回Container的实现代码看“Put函数”,它里面也用到了一种Visitor。实际上,Visitor是对操作(算法)的一种抽象,不针对具体对象,故它可以用于Accept函数,也可用在其他地方,提高代码复用。这就是Visitor Pattern的优势。


三、Visitor类与仿函数

        先看“PuttingVisitor”,它的功能其实可以用一个函数指针来代替。设计一个函数指针,它接受一个Object &参数。再看“MatchingVisitor”,它的功能其实也可以用一个函数对象来代替,只需要在这个函数对象构造的时候给它传递一个参数。此外,使用C++11中的std::function(函数对象包装器)和std::bind,可以将这种设计变得更通用化。


0 0