C++、多态性和迭代器

发布于 2024-10-03 10:01:24 字数 582 浏览 0 评论 0原文

我想要一个存储接口(抽象类)和一组存储实现(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 ,但随后我需要使用动态内存并在整个存储空间中分配地方。

有什么提示吗?

I want to have a Storage interface(abstract class) and a set of Storage implementations (SQLite, MySQL, Memcached..) for storing objects of a known class and retrieving subsets from the Storage.
To me the clear interface would be:

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;
};

And then create implementations of Storage that fulfill the interface. Now, my problem is the following:

  • Iterators can't be polymorphic as they are returned by value.
  • I can't just subclass Storage::iterator for my given Storage implementation
  • I thought about having a wrapper iterator that wraps and does pimpl over a polymorphic type that the Storage implementations subclass, but then I need to use dynamic memory and allocate all over the place.

Any hint?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

南…巷孤猫 2024-10-10 10:01:24

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

#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-> (如果包含的内容则需要后者) type 是类类型)。

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

If you want a virtual interface for iteration, something like this?

#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";
    }
}

This isn't a complete solution - I haven't implemented Iterator::operator==, or Iterator::operator-> (the latter is needed if the contained type is a class type).

I'm storing state in the ConcreteStorage class, which means we can't have multiple iterators on the same Storage at the same time. So probably rather than Iterable being a base class of Storage, there needs to be another virtual function of Storage to return a new Iterable. The fact that it's only an input iterator means that copies of an iterator can all point to the same Iterable, so that can be managed with a shared_ptr (and either Itertable should have a virtual destructor, or the newIterator function should return the shared_ptr, or both).

锦欢 2024-10-10 10:01:24

我没有看到存储多态性的好处。

无论如何,请注意迭代器根本不必是多态的。

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

I fail to see the benefit in Storage being polymorphic.

Anyway, note that the iterator doesn't have to polymorphic at all.

It just has to use virtual methods from the Storage class for it's functionality. These methods can be then easily overridden in the descendants (creating desired functionality).

沒落の蓅哖 2024-10-10 10:01:24

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

I'm not exactly sure why this is a problem. You just need to implement all the iterator operators (increment, dereference, etc.) so that they call a virtual method of the Storage object.

墨小墨 2024-10-10 10:01:24

您使用数据库引擎来进行存储的事实并没有改变您所拥有的本质上是一个容器类的事实。

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

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

The fact that you're using a database engine to do the storing doesn't change the fact that what you have here is fundamentally a container class.

In short, you should almost certainly be using a class template that's instantiated over the type of object being stored. The variation in storage engines could be handled either via inheritance or by a second template argument. Using a template argument gives compile-time polymorphism, while inheritance gives run-time polymorphism (i.e., you can change storage engines at run-time).

You might want to look at DTL for some inspiration (or you might save yourself a lot of trouble, and just use it until or unless you run into a problem with it).

焚却相思 2024-10-10 10:01:24

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

It seems to be a case of dynamic allocation versus ability to change the iterator's state size (without recompiling clients).

帅的被狗咬 2024-10-10 10:01:24

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

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

You may try boost::iterator, several adapters and facades are provided.

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

白鸥掠海 2024-10-10 10:01:24

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

Take a look at adobe::any_iterator or some other implementation of any_iterator (http://thbecker.net/free_software_utilities/type_erasure_for_cpp_iterators/any_iterator.html). It implements a polymorphic iterator concept but you still deal with any_iterator by value (return by value, pass by value, etc).

ζ澈沫 2024-10-10 10:01:24

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

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

Your algorythms' code that actualy uses different types of containers has to be written in template functions (possibly member functions), who get the types through the template's parameters.
That resolves at compile time. And the exact iterator type is known for each instanciation of that template function.

If you really need a runtime resolution, you need to add a dispatch to call different instanciations of the above template function. A virtual function should call the actual algorythm template function, but that would be a virtual function override in some template class (i.e. the virtual function will be compiled separately for each instanciation and call a different instanciation of the algorythm template function).
If you need a double/multiple dispatch, so be it. Too bad that c++ doesn't support function to be virtual on multiple parameters, you'll have to use any of the common idioms for double dispatch. But the call for the actual algorythm function should be after the dispatch is resolved.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文