【问题标题】:C++ Deriving from interface with nested classC ++从具有嵌套类的接口派生
【发布时间】:2023-03-07 15:33:01
【问题描述】:

我正在编写代码来处理“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

【问题讨论】:

  • 您没有描述您需要解决的问题。这个想法听起来很糟糕,但没有上下文就无法判断它是否有价值。
  • 很明显,在这里使用FooOne foo1; FooTwo foo2; foo2.say_element(foo1.getElement()) 将是一个巨大的错误。您是否有可能拥有FooThree,在可能且有时需要拥有foo3.say_element(foo2.getElement()) 的地方?或者强制各种元素类都具有不同的类型会更好吗?
  • '我的理解是应该避免使用void指针'如果你有接口,一般不需要备份@ 987654334@指针?!?

标签: c++ inheritance interface


【解决方案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);
    }
};

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

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

      否则,您可以使用模板来定义具有不同类型参数的相同函数。 参考这个,真的很有用

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

      谢谢你

      【讨论】:

        【解决方案4】:

        你想要一个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());
                }
            }
        }
        

        【讨论】:

        • 感谢您冗长而详细的回答。不幸的是,我在描述我的问题时做得很糟糕。关于在元素本身中实现容器元素的访问,我可能也不是很清楚。假设我有一个Graph,它在内部将节点存储在一个向量中。如果Graph::Node只是简单的包装了节点的索引,我可以支持O(1)查找。但是另一种实现可能会将节点存储在列表中,因此Graph::Node 可以包装指向节点对象的指针。所以,不,元素没有进行访问,但它们确实包装了某种类型的索引或迭代器。
        猜你喜欢
        • 1970-01-01
        • 2010-12-12
        • 1970-01-01
        • 2014-12-01
        • 2017-12-26
        • 2014-11-26
        • 1970-01-01
        • 2018-08-09
        • 2014-06-18
        相关资源
        最近更新 更多