C ++从具有嵌套类的接口派生

时间:2014-02-06 18:51:24

标签: c++ inheritance interface

我正在编写代码来处理“Foo”类型的对象。 foo是一种容器,为了提供对其元素的高效和抽象访问,它提供了一个类型为Element的嵌套类。 Element包装对象在容器中的位置。

现在,可能有不同的“Foo”实现,所以我正在编写一个抽象基类FooInterface来为它们提供一个通用接口。问题是每个实现可能需要定义自己的Element类型。例如,一个实现可以将其数据保存在向量中,使其Element包装向量迭代器,而另一个实现包含一个列表,并且其Element包装一个列表迭代器。

我已经制定了一个使用void指针工作的解决方案。本质上,基类定义了一个包含void指针的Element类。 FooInterface的不同实现可以将void指针转换为它们用于表示元素的任何类型。暂时忽略内存泄漏:

class FooInterface
{
    public:
    class Element {
        void* payload;
        public:
        Element(void* payload) : payload(payload) {}
        void* getPayload() const { return payload; } 
    };

    virtual void say_element(Element) = 0;
    virtual Element getElement() = 0;
};


class FooOne : public FooInterface
{
    public:
    virtual void say_element(Element element)
    {
        std::cout << "FooOne says: " << 
            * (int *) element.getPayload() << "." << std::endl;
    }

    virtual Element getElement()
    {
        return Element(new int(42));
    }
};


class FooTwo : public FooInterface
{
    public:
    virtual void say_element(Element element)
    {
        std::cout << "FooTwo says: " << 
            * (std::string*) element.getPayload() << "." << std::endl;
    }

    virtual Element getElement()
    {
        return Element(new std::string("This is a test"));
    }
};


void say(FooInterface& foo)
{
    FooInterface::Element el = foo.getElement();
    foo.say_element(el);
}


int main()
{

    FooOne foo_one;
    FooTwo foo_two;

    say(foo_one);
    say(foo_two);

    return 0;
}

虽然这有效,但似乎必须有更好的方法。我的理解是,如果可能的话,应该避免使用void指针。那么,这是实现这一目标的最佳方式吗?

编辑:

我在这篇文章中描述了我正在尝试做的事情。然而,答案有助于我思考,而且我已经设计出了我认为是一个不错的解决方案here

4 个答案:

答案 0 :(得分:1)

您可以将FooInterface设为模板并传递Element应存储的模板参数:

template <typename Payload>
class FooInterface
{
    public:
    class Element {
        Payload payload;
        public:
        Element(Payload payload) : payload(payload) {}
        Payload getPayload() const { return payload; } 
    };

    virtual void say_element(Element) = 0;
    virtual Element getElement() = 0;
};

您的子类必须将相应的模板参数传递给超类:

class FooOne : public FooInterface<int>
{
    public:
    virtual void say_element(Element element)
    {
        std::cout << "FooOne says: " << 
            element.getPayload() << "." << std::endl;
    }

    virtual Element getElement()
    {
        return Element(42);
    }
};

答案 1 :(得分:0)

如果您的目标是让每个容器始终将其元素类型公开为FooXXX::Element,那么您可以对FooInterface进行逐字化,并为Element添加typedef:

template<class elem>
class FooInterface {
public:
    typedef elem Element;
    /*... */
};

class FooOneElement {
    /*... */
};

class FooOne : public FooInterface<FooOneElement> {
    /*... */
};

答案 2 :(得分:0)

如果您知道将要使用的不同类型的数据类型,为什么不在派生类中声明相应的变量然后使用它们?基类中不需要void *。另外我认为,您可以避免在所有派生类中使用virtual关键字,而是可以在要在基类中使用的函数之前指定virtual。

否则您可以使用模板来定义具有不同类型的一个参数的相同功能。 请参阅此内容,它非常有用

http://www.codeproject.com/Articles/5351/Simulation-of-Virtual-Function-with-Template

感谢你

答案 3 :(得分:0)

你想要一个container, [...] to provide efficient and abstracted access to its elements。这是通用(虚拟)和。之间的权衡 高效(模板)实施。由于抽象,您将失去效率(与标准容器相比)。将元素的访问和迭代放入容器的元素中(在我看来)是完全错误的设计。此外,您的解决方案(如上所述)远非类型安全。

类型安全的解决方案可能是:

// Library
// =======

#include <memory>
#include <stdexcept>

template <typename Element>
class BasicElements
{
    public:
    typedef Element element_type;

    protected:
    struct Implementation {
        virtual element_type& get() = 0;

        virtual bool first() = 0;
        virtual bool next() = 0;
        virtual bool last() = 0;
        virtual void end_of_elements() = 0;

        virtual void insert(const element_type&) = 0;
    };

    template <typename Container>
    struct BidirectionalImplementation : Implementation {
        typedef Container container_type;
        typedef typename container_type::value_type value_type;
        typedef typename container_type::iterator iterator;
        container_type elements;
        iterator position;

        BidirectionalImplementation()
        :   position(elements.begin())
        {}

        virtual bool first() {
            position = elements.begin();
            return (position != elements.end());
        }

        virtual bool next() {
            if(position != elements.end())
                return (++position != elements.end());
            return false;
        }

        virtual bool last() {
            position = elements.end();
            if(position != elements.begin()) {
                --position;
                return true;
            }
            return false;
        }

        virtual void end_of_elements() {
            position = elements.end();
        }
    };

    template <typename Container>
    struct SequentialImplementation : BidirectionalImplementation<Container> {
        private:
        typedef BidirectionalImplementation<Container> Base;

        public:
        typedef typename Base::container_type container_type;
        typedef typename Base::value_type value_type;
        typedef typename Base::iterator iterator;

        public:
        virtual element_type& get() {
            if(this->position == this->elements.end())
                throw std::out_of_range("Position");
            return *this->position;
        }

        virtual void insert(const element_type& e) {
            this->position = this->elements.insert(this->position, e);
            ++this->position;
        }
    };

    template <typename Container>
    struct MapImplementation : BidirectionalImplementation<Container> {
        private:
        typedef BidirectionalImplementation<Container> Base;

        public:
        typedef typename Base::container_type container_type;
        typedef typename Base::value_type value_type;
        typedef typename Base::iterator iterator;

        public:
        virtual element_type& get() {
            if(this->position == this->elements.end())
                throw std::out_of_range("Position");
            return this->position->second;
        }

        virtual void insert(const element_type& e) {
            this->position = this->elements.insert(this->position, value_type(e.key(), e));
        }
    };

    template <typename Other>
    BasicElements(std::shared_ptr<Other> p)
    :   m_self(std::static_pointer_cast<Implementation>(p))
    {}

    public:
    element_type& get() { return m_self->get(); }
    const element_type& get() const { return m_self->get(); }

    bool first() const { return m_self->first(); }
    bool next() const { return m_self->next(); }
    bool last() const { return m_self->last(); }
    void end_of_elements() const { m_self->end_of_elements(); }

    void insert(const element_type& e) { m_self->insert(e); };

    private:
    std::shared_ptr<Implementation> m_self;
};

/// PRECONDITION The container fulfills (a subset of) the requirements
///              for a standard (bidirectional) sequence container.
template <typename Container>
class SequentialElements : public BasicElements<typename Container::value_type>
{
    private:
    typedef BasicElements<typename Container::value_type> Base;

    protected:
    typedef typename Base::template SequentialImplementation<Container> Implementation;

    public:
    SequentialElements()
    :   Base(std::make_shared<Implementation>())
    {}
};

/// PRECONDITION The container fulfills (a subset of) the requirements
///              for a standard (bidirectional) associative container.
///              The mapped type of the container has a member K key() const,
///              where K is convertable to the key_type of the container.
template <typename Container>
class MapElements : public BasicElements<typename Container::mapped_type>
{
    private:
    typedef BasicElements<typename Container::mapped_type> Base;

    protected:
    typedef typename Base::template MapImplementation<Container> Implementation;

    public:
    MapElements()
    :   Base(std::make_shared<Implementation>())
    {}
};


// Test
// ====

#include <iostream>
#include <list>
#include <vector>
#include <map>

class Element
{
    public:
    Element(int value)
    :   m_key(value), m_value(value)
    {}
    Element(int key, int value)
    :   m_key(key), m_value(value)
    {}

    int key() const { return m_key; }
    const int& value() const { return m_value; }
    int& value() { return m_value; }

    private:
    int m_key;
    int m_value;
};

inline bool operator < (const Element& a, const Element& b) {
    return a.key() < b.key();
}


int main() {
    typedef SequentialElements< std::vector<Element> > vector_elements;
    typedef SequentialElements< std::list<Element> > list_elements;
    typedef MapElements< std::map<int, Element> > map_elements;

    vector_elements v;
    v.insert(Element(2));
    v.insert(Element(1));
    v.insert(Element(0));

    list_elements l;
    map_elements m;

    if(v.first()) {
        do {
            l.insert(v.get());
            m.insert(v.get().value());
        }
        while(v.next());
    }

    const char* ContainerName[] = {
        "Vector",
        "List",
        "Map"
    };

    typedef std::vector<BasicElements<Element>> container_collection;
    container_collection collection;
    collection.push_back(v);
    collection.push_back(l);
    collection.push_back(m);
    for(unsigned i = 0; i < collection.size(); ++i) {
        const BasicElements<Element>& elements = collection[i];
        std::cout << ContainerName[i] << "\n";
        if(elements.first()) {
            do {
                std::cout << elements.get().value() << '\n';
            }
            while(elements.next());
        }
    }
}