Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/159.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/symfony/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 基于接口的C+编程+;与迭代器结合使用。如何保持这个简单?_C++_Design Patterns_Interface_Iterator - Fatal编程技术网

C++ 基于接口的C+编程+;与迭代器结合使用。如何保持这个简单?

C++ 基于接口的C+编程+;与迭代器结合使用。如何保持这个简单?,c++,design-patterns,interface,iterator,C++,Design Patterns,Interface,Iterator,在我的开发过程中,我正慢慢地从面向对象的方法转向基于接口的编程方法。更准确地说: 过去,如果我能将逻辑分组到一个类中,我已经很满意了 现在我倾向于在接口后面添加更多逻辑,让工厂创建实现 一个简单的例子说明了这一点 过去我写过这些课程: 图书馆 书 现在我写这些类: 图书馆 图书馆 图书馆工厂 IBook 书 书厂 这种方法允许我为我的每个接口轻松地实现模拟类,在旧的、较慢的实现和新的、较快的实现之间切换,并在同一个应用程序中比较它们 在大多数情况下,这非常有效,但如果我想使用迭代器在

在我的开发过程中,我正慢慢地从面向对象的方法转向基于接口的编程方法。更准确地说:

  • 过去,如果我能将逻辑分组到一个类中,我已经很满意了
  • 现在我倾向于在接口后面添加更多逻辑,让工厂创建实现
一个简单的例子说明了这一点

过去我写过这些课程:

  • 图书馆
现在我写这些类:

  • 图书馆
  • 图书馆
  • 图书馆工厂
  • IBook
  • 书厂
这种方法允许我为我的每个接口轻松地实现模拟类,在旧的、较慢的实现和新的、较快的实现之间切换,并在同一个应用程序中比较它们

在大多数情况下,这非常有效,但如果我想使用迭代器在集合上循环,这将成为一个问题

假设我的图书馆有一套书,我想对它们进行迭代。过去这不是问题:Library::begin()和Library::end()返回了一个迭代器(Library::iterator),我可以轻松地在其上编写循环,如下所示:

for (Library::iterator it=myLibrary.begin();it!=mylibrary.end();++it) ...
ILibraryBookFinder *myFinder = LibraryBookFinderFactory (FINDER_POPULARITY);
for (ILibraryBookFinder::const_iterator it=myFinder->begin();it!=myFinder.end();++it) ...
问题是,在基于接口的方法中,不能保证不同的ILibrary实现使用相同类型的迭代器。如果OldLibrary和NewLibrary都继承自ILibrary,则:

  • OldLibrary可以使用std::vector来存储其书籍,并在其begin和end方法中返回std::vector::const_迭代器
  • NewLibrary可以使用std::list来存储其书籍,并在其begin和end方法中返回std::list::const_迭代器
要求两个ILibrary实现返回相同类型的迭代器也不是一个解决方案,因为在实践中,增量操作(++it)需要在两个实现中以不同的方式实现

这意味着在实践中,我必须使迭代器也成为接口,这意味着应用程序不能将迭代器放在堆栈上(典型的C++切片问题)。 我可以通过将迭代器接口封装在一个非接口类中来解决这个问题,但对于我试图实现的内容来说,这似乎是一个相当复杂的解决方案

有没有更好的方法来处理这个问题

编辑: 马丁发言后的一些澄清

假设我有一个类返回所有按流行程度排序的书籍:LibraryBookFinder。 它有begin()和end()方法,返回引用书籍的LibraryBookFinder::const_迭代器

要用全新的实现替换旧的实现,我想将旧的LibraryBookFinder放在接口ILibraryBookFinder后面,并将旧的实现重命名为OldSlowLibraryBookFinder

然后,我的新(快速膨胀的)实现VeryFastCachingLibraryBookFinder可以从ILibraryBookFinder继承。这就是迭代器问题的来源

下一步可能是将界面隐藏在工厂后面,在那里我可以问工厂“给我一个‘查找器’,它非常擅长根据受欢迎程度、标题或作者……返回书籍。您最终得到的代码如下:

for (Library::iterator it=myLibrary.begin();it!=mylibrary.end();++it) ...
ILibraryBookFinder *myFinder = LibraryBookFinderFactory (FINDER_POPULARITY);
for (ILibraryBookFinder::const_iterator it=myFinder->begin();it!=myFinder.end();++it) ...
或者如果我想使用其他标准:

ILibraryBookFinder *myFinder = LibraryBookFinderFactory (FINDER_AUTHOR);
for (ILibraryBookFinder::const_iterator it=myFinder->begin();it!=myFinder.end();++it) ...

LibraryBookFinderFactory的参数可能由外部因素决定:配置设置、命令行选项、对话框中的选择等等,每个实现都有自己的优化(例如,一本书的作者不会改变,所以这可能是一个相当静态的缓存;流行度每天都可以改变,这可能意味着一个完全不同的数据结构)。

如果你的图书馆拥有大量的书,你应该考虑把你的“聚合”函数放进你的集合中,并在动作中传递它要执行的东西。 具有以下性质的事物:

class ILibrary
{
public:
  virtual ~Ilibrary();
  virtual void for_each( boost::function1<void, IBook> func ) = 0;
};

LibraryImpl::for_each( boost::function1<void, IBook> func )
{
    std::for_each( myImplCollection.begin(), myImplCollection.end(), func );
}
class-ILibrary
{
公众:
虚拟~Ilibrary();
每个函数的虚拟空(boost::function1 func)=0;
};
LibraryImpl::for_each(boost::function1 func)
{
std::for_each(myImplCollection.begin()、myImplCollection.end()、func);
}
虽然可能不完全是这样,因为您可能需要使用共享的ptr、constness等。出于这个目的(或者通常在我大量使用接口的实现中),我还为迭代器创建了一个接口,其他对象返回这个接口。它变得非常类似Java

如果您关心在大多数情况下使用迭代器:您的问题当然是您在编译时不知道迭代器的大小,因此无法分配正确大小的堆栈变量。但是如果您确实非常关心这一点:也许您可以编写一些包装器,在sta上分配特定大小ck(例如128字节),如果新的迭代器适合,它会将其移动到那里(确保迭代器有一个正确的接口,以干净的方式允许此操作)。或者您可以使用
alloca()
。例如,您的迭代器接口可能类似于:

struct IIterator {
   // iterator stuff here
   // ---
   // now for the handling on the stack
   virtual size_t size() = 0; // must return own size
   virtual void copyTo(IIterator* pt) = 0;
};
还有你的包装纸:

struct IteratorWrapper {
   IIterator* pt;
   IteratorWrapper(IIterator* i) {
       pt = alloca(i->size());
       i->copyTo(pt);
   }
   // ...
};
大概吧


另一种方法是,如果理论上编译时总是很清楚(不确定这是否适用;这是一个明确的限制):在任何地方都使用函子。这有许多其他缺点(主要是在头文件中包含所有真实代码),但您的代码速度非常快。例如:

template<typename T>
do_sth_with_library(T& library) {
   for(typename T::iterator i = library.begin(); i != library.end(); ++i)
      // ...
}
模板
用图书馆做某事(T&library){
for(typename T::iterator i=library.begin();i!=library.end();++i)
// ...
}
但是如果你过于依赖它,代码可能会变得非常难看


CashCow提供了另一个很好的解决方案(使代码更加实用——为每个接口实现

使用当前C++,这会使代码使用起来有点复杂/难看。随着即将到来的C++ 0x和lambda函数,这个解决方案会变得更加干净。