【问题标题】:Is there any way to modify a nested classes implementation in it's children?有没有办法在它的孩子中修改嵌套类的实现?
【发布时间】:2020-04-08 20:23:08
【问题描述】:

我想要一个类接口(我们称之为A类),它可以在我的代码中以不同的方式继承和实现,并且该接口包含一些函数,包括一个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 上实现,另一个直接在文件上实现。这意味着向量存储库的迭代器必须只给我 const std::vector 迭代器,而另一个必须以只读方式打开文件、转到下一行等。我必须使它们与基于范围的 for 循环兼容。

【问题讨论】:

  • 很抱歉。我取消了评论,因为我不确定我是否理解这个问题,而且我的替换评论需要很长时间才能写出来。
  • 没关系。我的 B::iterator 是在一个 cpp 文件中实现的,与这个头文件分开。但这不重要,问题是程序说 B::begin() 和 B::end() 不返回与 A::begin() 和 A::end() 相同的类型,这是正确的,因为在 A 中我返回一个 A::iterator,而在 B 中我返回一个 B::iterator。但是 B::iterator 继承自 A::iterator,这也使它成为 A::iterator,所以我不知道问题是什么以及为什么它不想这样编译它。
  • 您希望A 的所有子级都实现一个迭代器。你已经做到了。所有子迭代器都可以不同,但​​仍将支持A::iterator 定义的通用接口。我们这里有一个X-Y problem。你有一个解决问题的方法,这个问题正在制造更多的问题。您最好修改问题以询问您尝试使用此解决方案解决的问题。
  • 做了编辑,很抱歉不清楚我想要实现的目标。
  • 由于 is-a 关系,您可以使用对 B::iteratorA::iterator 的引用,但这很奇怪。迭代器应该像那样掩盖蓝精灵。如果添加另一层抽象会怎样?在这种情况下,A::iterator 并不是真正的迭代器。它是一个包装器,它隐藏了它持有对 B::iterator(或任何实现 A::private_iterator 接口的任何东西)的引用这一事实。

标签: c++ inheritance polymorphism inner-classes


【解决方案1】:

您似乎正在寻找一种类型擦除:一种支持不同实现的类型,但可以被其他代码用作单一类型,因为可以复制该类型,而不仅仅是通过指针和引用,等等。通常的设置方法是一个非多态类,它包含一个指向多态接口的指针:

#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()); }
};

【讨论】:

    【解决方案2】:

    通过返回对迭代器而不是对象的引用,我设法直截了当地解决了这个问题。有了这个,我总是必须保留一个我可以在主函数中引用和返回的指针,但至少它可以工作。问题就像 cmets 中提到的那样:多态性仅适用于指针和引用。

    【讨论】:

      猜你喜欢
      • 2020-11-07
      • 1970-01-01
      • 2015-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多