Есть ли способ изменить реализацию вложенных классов в его дочерних? - PullRequest
0 голосов
/ 08 апреля 2020

Я хочу иметь интерфейс класса (назовем его классом А), который может наследоваться и реализовываться по-разному в моем коде, и этот интерфейс содержит некоторые функции, включая итератор типа stl, который определяется как вложенный класс.

Моя проблема в том, что всякий раз, когда я пытаюсь унаследовать интерфейс (в класс давайте вызовем B), а также итератор и реализовать их, начало и конец унаследованного класса ожидают итератор типа A :: iterator, хотя итератор в классе B наследуется от A :: iterator. Я знаю, что полиморфизм работает только с указателями / ссылками, но как я смогу это сделать?

Я также пытался делать это с классами друзей, но я не очень хорошо знаю, как они работают, как я никогда не использовал их раньше, так получилось немного грязно. Вот пример кода (который я действительно запускаю)

class A
{
public:
    class iterator 
    {
    public:
        virtual Entity& operator*() const = 0;
        virtual bool operator!=(const iterator) const = 0;
        virtual bool valid() const = 0;
        virtual iterator& operator=(const iterator& it) = 0;
        virtual iterator operator++() = 0;
        virtual iterator operator++(int) = 0;
        virtual ~iterator() {};
    };
    virtual int add(const std::vector<std::string>& params) = 0;
    virtual int remove(const std::vector<std::string>& params) = 0;
    virtual int update(const std::vector<std::string>& params) = 0;
    virtual int save(const std::vector<std::string>& params) = 0;
    virtual int size() = 0;
    virtual typename iterator begin() = 0;
    virtual typename iterator end() = 0;
    virtual ~A() {}
};


class B: public A
{
private:
    std::vector<Entity> elements;
public:
    class iterator : public A::iterator
    {
    private:
        std::vector<Entity>::iterator ptr;
        B& repo;
    public:
        iterator(std::vector<Entity>::iterator ptr, B& container) : ptr{ ptr }, repo{ container }{}
        Entity& operator*() const override;
        bool operator!=(const iterator) const override;
        bool valid() const override;
        iterator& operator=(const iterator& it) override;
        iterator operator++() override;
        iterator operator++(int) override;
    };
    B() : elements{ std::vector<Entity>() } {}
    int add(const std::vector<std::string>& params) override;
    int remove(const std::vector<std::string>& params) override;
    int update(const std::vector<std::string>& params) override;
    int save(const std::vector<std::string>& params) override;
    int size() override;
    typename iterator begin();
    typename iterator end();
    ~B() {};
};

Причина, по которой я хочу это сделать, заключается в том, что мне нужно создать 2 разных репозитория, один из которых работает с файлом, а другой - в памяти, и возможно, в будущем потребуется база данных, но я просто не могу получить правильный итератор, унаследовав его. Возвращение std :: vector будет намного быстрее, но также и обманом. РЕДАКТИРОВАТЬ: То, что я в конечном итоге пытаюсь достичь, - это иметь два репозитория с одинаковым интерфейсом, которые также можно перебирать, один из которых реализован на std :: vector, а другой - непосредственно в файле. Это означает, что итератор для векторного репозитория должен будет просто дать мне константный std :: vector итератор, а другой должен открыть файл, go до следующей строки и т. Д. c только для чтения , Я должен сделать их совместимыми с диапазоном, основанным на l oop.

Ответы [ 2 ]

1 голос
/ 16 апреля 2020

Похоже, вы ищете стирание типа: тип, который поддерживает различные реализации, но может использоваться другим кодом как простой тип, так как этот тип, а не только указатель и ссылка, может быть скопирован, и др c. Обычный способ настроить это - класс non-polymorphi c, который содержит указатель на интерфейс polymorphi c:

#include <utility>
#include <memory>
#include <vector>

struct Entity {};

class A
{
protected:
    class IterInterface
    {
    public:
        virtual ~IterInterface() = default;
        virtual std::unique_ptr<IterInterface> clone() const = 0;
        virtual Entity& dereference() const = 0;
        virtual void increment() = 0;
        virtual bool equal_to(const IterInterface& other) const = 0;
    };

    virtual std::unique_ptr<IterInterface> begin_impl() = 0;
    virtual std::unique_ptr<IterInterface> end_impl() = 0;

public:
    class iterator
    {
    public:
        using iterator_category = std::forward_iterator_tag;
        using value_type = Entity;
        using reference = Entity&;
        using pointer = Entity*;
        using difference_type = std::ptrdiff_t;

        iterator() = default;
        iterator(const iterator& it)
        {
            if (it.m_impl)
                m_impl = it.m_impl->clone();
        }
        iterator(iterator&&) noexcept = default;
        iterator& operator=(const iterator& it)
        {
            if (it.m_impl)
                m_impl = it.m_impl->clone(); // self-assignment-safe
            else
                m_impl = nullptr;
            return *this;
        }
        iterator& operator=(iterator&&) noexcept = default;

        explicit iterator(std::unique_ptr<IterInterface> && impl)
            : m_impl(std::move(impl)) {}

        Entity& operator*() const
        { return m_impl->dereference(); }

        iterator& operator++()
        { m_impl->increment(); return *this; }
        iterator operator++(int) const
        {
            iterator copy = *this;
            m_impl->increment();
            return copy;
        }

        friend bool operator==(const iterator &it1, const iterator &it2)
        {
            bool it1_ok(it1.m_impl);
            bool it2_ok(it2.m_impl);
            if (it1_ok && it2_ok)
                return it1.m_impl->equal_to(*it2.m_impl);
            else
                return it1_ok == it2_ok;
        }
        friend bool operator!=(const iterator &it1, const iterator &it2)
        { return !(it1 == it2); }

    private:
        std::unique_ptr<IterInterface> m_impl;
    };

    iterator begin()
    { return iterator(begin_impl()); }
    iterator end()
    { return iterator(end_impl()); }

    // ...
};

class B : public A
{
public:
    // ...
private:
    std::vector<Entity> elements;

    class IterImpl : public IterInterface
    {
    public:
        explicit IterImpl(std::vector<Entity>::iterator viter)
            : m_viter(viter) {}

        std::unique_ptr<IterInterface> clone() const override
        { return std::make_unique<IterImpl>(m_viter); }

        Entity& dereference() const override { return *m_viter; }

        void increment() override { ++m_viter; }

        bool equal_to(const IterInterface& other) const override
        {
            if (auto* oimpl = dynamic_cast<const IterImpl*>(&other))
                return m_viter == oimpl->m_viter;
            // "other" isn't even from a B.
            return false;
        }

    private:
        std::vector<Entity>::iterator m_viter;
    };

    std::unique_ptr<IterInterface> begin_impl() override
    { return std::make_unique<IterImpl>(elements.begin()); }

    std::unique_ptr<IterInterface> end_impl() override
    { return std::make_unique<IterImpl>(elements.end()); }
};
0 голосов
/ 16 апреля 2020

Мне удалось решить проблему, прямо скажем так, возвращая ссылки на итератор, а не на объекты. При этом мне всегда нужно сохранять указатель, на который я могу ссылаться и возвращать в основной функции, но по крайней мере это работает. Проблема в том, что кто-то упоминал в комментариях: полиморфизм работает только с указателями и ссылками.

...