【问题标题】:C++, polymorphism and iteratorsC++、多态和迭代器
【发布时间】:2011-05-13 22:30:21
【问题描述】:

我想要一个存储接口(抽象类)和一组存储实现(SQLite、MySQL、Memcached..),用于存储已知类的对象并从存储中检索子集。
对我来说,清晰的界面是:

class Storable{int id; blah; blah; blah; string type;};
class Storage{
    virtual Storage::iterator get_subset_of_type(string type) = 0;
    virtual Storage::iterator end)_ = 0;
    virtual void add_storable(Storable storable) = 0;
};

然后创建满足接口的存储实现。现在,我的问题如下:

  • 迭代器不能是多态的,因为它们是按值返回的。
  • 我不能只为给定的 Storage 实现子类 Storage::iterator
  • 我曾考虑使用一个包装迭代器来包装并在 Storage 实现子类的多态类型上执行 pimpl,但随后我需要使用动态内存并在所有地方进行分配。

有什么提示吗?

【问题讨论】:

    标签: c++ class iterator polymorphism subclass


    【解决方案1】:

    如果你想要一个用于迭代的虚拟接口,像这样?

    #include <iostream>
    #include <iterator>
    
    struct Iterable {
        virtual int current() = 0;
        virtual void advance() = 0;
      protected:
        ~Iterable() {}
    };
    
    struct Iterator : std::iterator<std::input_iterator_tag,int> {
        struct Proxy {
            int value;
            Proxy(const Iterator &it) : value(*it) {}
            int operator*() { return value; }
        };
        Iterable *container;
        Iterator(Iterable *a) : container(a) {}
        int operator*() const { return container->current(); }
        Iterator &operator++() { container->advance(); return *this; }
        Proxy operator++(int) { Proxy cp(*this); ++*this; return cp; }
    };
    
    struct AbstractStorage : private Iterable {
        Iterator iterate() {
            return Iterator(this);
        }
        // presumably other virtual member functions...
        virtual ~AbstractStorage() {}
    };
    
    struct ConcreteStorage : AbstractStorage {
        int i;
        ConcreteStorage() : i(0) {}
        virtual int current() { return i; }
        virtual void advance() { i += 10; }
    };
    
    int main() {
        ConcreteStorage c;
        Iterator x = c.iterate();
        for (int i = 0; i < 10; ++i) {
            std::cout << *x++ << "\n";
        }
    }
    

    这不是一个完整的解决方案 - 我还没有实现 Iterator::operator==Iterator::operator-&gt;(如果包含的类型是类类型,则需要后者)。

    我将状态存储在 ConcreteStorage 类中,这意味着我们不能同时在同一个 Storage 上拥有多个迭代器。所以可能不是Iterable 是Storage 的基类,而是需要Storage 的另一个虚函数来返回一个新的Iterable。它只是一个输入迭代器这一事实意味着迭代器的副本都可以指向同一个Iterable,因此可以使用shared_ptr 进行管理(Itertable 应该有一个虚拟析构函数,或者 newIterator 函数应该返回shared_ptr,或者两者都返回)。

    【讨论】:

      【解决方案2】:

      我看不到存储多态的好处。

      无论如何,请注意迭代器根本不需要多态。

      它只需要使用 Storage 类的虚方法来实现它的功能。然后可以在后代中轻松覆盖这些方法(创建所需的功能)。

      【讨论】:

      • 需要多态行为,因为我将有几个存储需要在运行时可交换,例如主辅助,它们可以是不同的类型。
      • @Arkaitz 你仍然不需要多态性,如果你不知道具体的存储实际上是什么/做什么,你就需要多态性。
      • 这就是重点,我可能正在使用一个主(sqlite)和一个辅助(memcached),然后在运行时交换它们,所以我需要使用公共接口并因此在那里使用多态性,因为在任何时候我都不知道我的主要或次要类型是什么。
      • 感谢这个提示,我能够弄清楚如何实现类似 Iterable&lt;T&gt; "interface": stackoverflow.com/a/67338686/3907364
      【解决方案3】:

      我不确定为什么会出现这个问题。您只需要实现所有迭代器运算符(增量、取消引用等),以便它们调用Storage 对象的虚方法。

      【讨论】:

        【解决方案4】:

        您使用数据库引擎进行存储这一事实并不会改变您在这里拥有的基本上是一个容器类的事实。

        简而言之,您几乎可以肯定使用在所存储对象类型上实例化的类模板。存储引擎的变化可以通过继承或第二个模板参数来处理。使用模板参数提供编译时多态性,而继承提供运行时多态性(即,您可以在运行时更改存储引擎)。

        您可能想查看DTL 以获得一些灵感(或者您可能会省去很多麻烦,并且在遇到问题之前一直使用它)。

        【讨论】:

        • 嗯,这正是我想要做的,使用继承来处理不同的引擎,我只是在如何正确迭代子集方面遇到问题,因为我不能只继承和覆盖迭代器。
        • @Arkaitz:就像我说的,你应该看看 DTL 的总体设计。无论如何,存储引擎的变化与存储类型的变化是正交的,后者决定了迭代器的类型。
        • 你可以,如果迭代器中的状态相同(即只是覆盖迭代器的方法)
        • @jerry,我认为他的观点是迭代器特定于存储,而不是存储?
        • @Jerry Coffin,您是说存储类型决定了迭代器?通过它自己?正如我所看到的那样,vector&lt;int&gt;::iteratorlist&lt;int&gt;::iterator 不同,尽管存储的类型是相同的。最后,迭代器::operators 必须以某种方式处理存储内部或委托。
        【解决方案5】:

        这似乎是动态分​​配与更改迭代器状态大小的能力(无需重新编译客户端)的情况。

        【讨论】:

        • 具体来说,如果需要能够在不重新编译使用迭代器的代码的情况下更改迭代器的状态大小,那么动态分配是不可避免的。否则,使用codesynthesis.com/~boris/blog/2010/07/20/… 的某些变体可能不会太难看。
        【解决方案6】:

        您可以尝试 boost::iterator,提供了几个适配器和外观。

        http://www.boost.org/doc/libs/1_45_0/libs/iterator/doc/index.html

        【讨论】:

          【解决方案7】:

          查看 adobe::any_iterator 或 any_iterator 的其他一些实现 (http://thbecker.net/free_software_utilities/type_erasure_for_cpp_iterators/any_iterator.html)。它实现了一个多态迭代器概念,但您仍然按值处理 any_iterator(按值返回、按值传递等)。

          【讨论】:

            【解决方案8】:

            实际上使用不同类型容器的算法代码必须编写在模板函数(可能是成员函数)中,模板函数通过模板的参数获取类型。 这在编译时解决。并且该模板函数的每个实例化都知道确切的迭代器类型。

            如果你真的需要运行时解析,你需要添加一个 dispatch 来调用上述模板函数的不同实例。虚函数应该调用实际的算法模板函数,但这将是某些模板类中的虚函数覆盖(即,将为每个实例单独编译虚函数并调用算法模板函数的不同实例)。 如果您需要双重/多重调度,那就这样吧。太糟糕了,c ++不支持函数在多个参数上是虚拟的,你必须使用任何常见的习惯用法来进行双重调度。但实际算法函数的调用应该在调度解决之后。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2019-03-26
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-01-11
              • 2015-10-05
              • 2015-10-04
              • 2012-10-20
              相关资源
              最近更新 更多