2012-03-30 10 views
7

मैं एक बहुत ही सरल फ़ाइल को प्रबंधित डेटाबेस है कि मूल रूप से इस तरह दिखता है लिखा है:एक std-like iterator के कस्टम कार्यान्वयन का एहसास कैसे करें?

void iterateFiles() 
{ 
    FileDB filedb("C:\\MyFiles"); 

    for (FileDB::iterator file_it = filedb.begin(); file_it != filedb.end(); ++file_it) 
    { 
     File f = *file_it; 
     // do something with file 
    } 
} 

मैं:

class FileDB 
{ 
public: 
    FileDB(std::string dir) : rootDir(dir) { } 

    void loadFile(std::string filename, File &file) const; 
    void saveFile(std::string filename, const File &file) const; 

private: 
    std::string rootDir; 
} 

अब मैं एक std::iterator का उपयोग कर की तरह डेटाबेस में निहित सभी फ़ाइलों के माध्यम से पुनरावृति करना चाहते हैं कुछ प्रश्नों के उत्तर पढ़ चुके हैं, कुछ std::iterator प्राप्त करने का सुझाव देते हैं, कुछ std::iterator_traits का उपयोग करने के लिए, लेकिन मुझे वास्तव में यह नहीं समझना है कि यह कैसे करें। कस्टम इटरेटर को कार्यान्वित करने का प्रयास करते समय संभवतः गलत क्या हो सकता है? और यह करने के लिए एक सरल लेकिन सुरुचिपूर्ण तरीका क्या है?

संपादित करें: कृपया बढ़ावा देने पर विचार न करें, मेरा प्रश्न अधिक वैचारिक प्रकृति का है।

संपादित करें 2:

FileDB इस तरह काम करता है:

  • rootDir

    • foo1
      • bar1
        • foo1bar1_1.txt
        • foo1bar1_2.txt
      • bar2
        • foo1bar2_1.txt
        • foo1bar2_2.txt
    • foo2

    • टेलीफोन

      +०१२३५१६४१०
      • Barm

        • fooNBarM_x.txt

तो बुनियादी तौर पर, मैं अपने नाम से एक फ़ाइल पा सकते हैं।

जैसा कि मेरा कंटेनर स्मृति में नहीं है, मेरे पास इसके डेटा के पॉइंटर्स नहीं हैं। तो मेरा विचार था कि फ़ाइल के पथ को इटरेटर में स्टोर करना था। इस तरह, मैं एक स्ट्रिंग तुलना के साथ operator== लागू कर सकता हूं, क्योंकि पथ अद्वितीय होना चाहिए। fileDB.end() से लौटाया गया इटरेटर एक खाली स्ट्रिंग होगा और operator*fileDB::loadFile() को इसके फ़ाइलपैथ के साथ कॉल करेगा।

मेरी सबसे बड़ी चिंता operator++ है।फ़ाइल नाम होने के बाद, मैं युक्त निर्देशिका ढूंढ सकता हूं और अगली फ़ाइल के लिए खोज कर सकता हूं, लेकिन यह वास्तव में अप्रभावी है। ऐसा करने के बारे में कोई विचार क्या है? या मैं पूरी तरह से अपनी पूरी अवधारणा के साथ गलत हूँ?

+3

सबसे आसान तरीका शायद 'iterator_facade' [Boost.Iterator] से (http://www.boost.org/doc/libs/release/libs/iterator/doc/iterator_facade.html) का प्रयोग है। –

+3

बूस्ट :: फाइल सिस्टम का उपयोग करना और भी आसान कोड का उपयोग करना होगा ... –

+1

ठीक है, मुझे पता होना चाहिए कि मुझे जानकारी जोड़नी चाहिए, कि मैं बूस्ट का उपयोग नहीं करना चाहता;) अधिकतर, क्योंकि मैं समझना चाहता हूं कि एक पुनरावर्तक कैसे काम करता है। – Ben

उत्तर

6
class FileDB 
{ 
class iterator; 

public: 
    FileDB(std::string dir) : m_rootDir(dir) { m_files = getFileTreeList(); } 

    void loadFile(std::string filename, File &file) const; 
    void saveFile(std::string filename, const File &file) const; 


    iterator begin() 
    { 
     return iterator(m_files.begin(), *this); 
    } 
    iterator end() 
    { 
     return iterator(m_files.end(), *this); 
    } 

private: 
    std::list<std::string> getFileTreeList() const; 

private:  
    std::string m_rootDir; 
    std::list<std::string> m_files; 
} 



class FileDB::iterator 
{ 
public: 
    iterator(std::list<std::string>::iterator pos, FileDB& owner) : m_pos(pos), m_owner(owner){} 

    bool operator==(const iterator& rhs) {return m_pos == rhs.m_pos;} 
    bool operator!=(const iterator& rhs) {return m_pos != rhs.m_pos;} 
    //etc 

    void operator++() {++m_pos;} 

    File operator*() 
    { 
    return openFile(*m_pos); // for example open some file descriptor 
    } 

    ~iterator() 
    { 
    closeFile(*m_pos); // clean up! 
    } 

private: 
    std::list<std::string>::iterator& m_pos; 
    FileDB& m_owner; 
}; 
+0

यह बहुत ही आशाजनक लग रहा है! इस तथ्य को छोड़कर कि मेरे डेटाबेस को संभावित रूप से लाखों फाइलों को स्टोर करना है, जो स्टार्टअप को वास्तव में धीमा कर देगा और फाइलडीबी काफी मेमोरी का उपभोग करेगा। – Ben

+1

हां, 'std :: iterator' से प्राप्त करना एक अच्छा विचार है। एक आभासी विनाशक की कमी कोई समस्या नहीं है। एक आभासी विनाशक केवल तभी जरूरी है जब आपको मूल प्रकार और किसी भी प्रकार के डेटा प्रबंधन के रूप में पॉलिमॉर्फिक व्यवहार की आवश्यकता हो। एक पुनरावर्तक को न तो चाहिए/न ही चाहिए। इसके अलावा आपका इटरेटर सभी मानक एल्गोरिदम के साथ असंगत है, क्योंकि यह आवश्यक टाइपिफ़ या गुणों की पेशकश नहीं करता है। आखिरकार एक अच्छा नियम है: अगर संदेह में एसटीएल के रूप में होता है। 'बिट्स/stl_iterator.h' देखें। सभी एसटीएल इटरेटर 'std :: iterator' – LiKao

+0

@LiKao से प्राप्त होते हैं: क्या मैं इस कोड को iterator_traits जोड़कर मानक एल्गोरिदम के साथ संगत बनाने के लिए संशोधित नहीं कर सकता? – Ben

11

खुद को लिखने वाले हीटर शायद ही कभी सुंदर हैं। अपने वर्गों में एक इटरेटर जोड़ने का सबसे पहला तरीका मौजूदा एक का पुन: उपयोग करना है। आपको अवगत होना चाहिए, उदाहरण के लिए पॉइंटर्स सी ++ में इटरेटर्स जितना अच्छा है, इसलिए वास्तव में अपना खुद का लिखने के बिना एक इटरेटर प्रदान करने के कई तरीके हैं।

यह मूल रूप से सी ++ कई तरीकों से कैसे काम करता है। यह लाइब्रेरी लेखकों पर बहुत अधिक बोझ डालकर अंतिम उपयोगकर्ताओं के लिए भाषा व्यय करने योग्य और सरल बनाने की कोशिश करता है। अर्थात। लाइब्रेरी लेखकों को सभी अनौपचारिक सामान लिख सकते हैं, इसलिए अंतिम उपयोगकर्ता को यह नहीं करना है। Iterators आंशिक रूप से एक पुस्तकालय का हिस्सा हैं।

कहा करने के बाद कि, यहाँ वास्तविक बदसूरत हिस्सा आता है:

अपनी खुद की iterators कुछ चीजें आप के बारे में पता करने की आवश्यकता है लिखने के लिए, यहाँ हैं सक्षम होने के लिए।

प्रकार लक्षण:

प्रकार लक्षण सी ++ जो प्रकार जो खुद को बदला नहीं जा सकता है के साथ भी काम करता है में प्रकार के aditional जानकारी जोड़ने के लिए एक सरल तंत्र हैं। उदाहरण के लिए एक पुनरावर्तक के लिए यह जानना महत्वपूर्ण है कि यह क्या हो रहा है (यानि निहित प्रकार)। दिए गए इटरेटर के लिए यह जानकारी प्राप्त करने का तरीका इटेटरेटर पर बहुत निर्भर करता है। वास्तव में ऑब्जेक्टर्स जो ऑब्जेक्ट्स हैं, आप कक्षा में टाइपिफ़ी जोड़ सकते हैं और उन का उपयोग कर सकते हैं, लेकिन इटरेटर जो पॉइंटर्स हैं, आपको पॉइंटर प्रकार से इसका अनुमान लगाने की आवश्यकता है। इसे संभव बनाने के लिए जानकारी को एक प्रकार की विशेषता में संग्रहीत किया जाता है, इसलिए एक ही स्थान एक कोड है जो इस जानकारी को देख सकता है। यह std::iterator_traits प्रकार विशेषता है।

std::iterator_traits किसी भी चीज़ पर काम करता है, जो std::iterator टेम्पलेट के साथ-साथ किसी भी प्रकार के पॉइंटर पर बिना किसी ट्विकिंग के व्युत्पन्न होता है। अक्सर अपने खुद के लक्षण विशेषज्ञता लिखने से बचने के लिए std::iterator आधार के रूप में उपयोग करना सबसे अच्छा है। यदि आप ऐसा नहीं कर सकते हैं, तो आवश्यक गुण प्रदान करना अभी भी संभव है, लेकिन यह कठिन होगा।

टैग वर्गों और इटरेटर प्रकार:

वहाँ सी में उपलब्ध iterators ++ जो अलग व्यवहार कर और/अलग अलग बातें की एक बहुत कुछ नहीं कर सकते कर सकते हैं के कई अलग अलग प्रकार के होते हैं। http://cplusplus.com/reference/std/iterator/ पर एक नज़र डालें, यह देखने के लिए कि किस प्रकार के इटरेटर उपलब्ध हैं और वे क्या कर सकते हैं। आरेख एक ऑब्जेक्ट उन्मुख तरीके से नहीं हैं (यानी input_iterator न तो उप-न ही forward_iterator का आधार वर्ग) है, बल्कि एक एपीआई प्रकार का व्युत्पन्न है। अर्थात। आप सभी एल्गोरिदम का उपयोग कर सकते हैं जो इनपुट इटरेटर के लिए एक आगे इटरेटर के साथ भी लिखे गए थे। पृष्ठ पर तालिका आपको बताएगी कि आपको प्रत्येक श्रेणी के लिए कौन सी विधियां प्रदान करनी होंगी।

चूंकि ये श्रेणियां वास्तव में एक-दूसरे के उप-वर्ग नहीं हैं (वे विशेष रूप से विभिन्न प्रकार के संग्रहों से आने पर नहीं होनी चाहिए), एक अन्य तंत्र का उपयोग प्रत्येक इटरेटर की क्षमताओं की पहचान के लिए किया जाता है। std::iterator_traits में प्रत्येक इटेटरेटर का वर्णन करने वाला एक खाली टैग क्लास भी शामिल है, जो बताता है कि यह इटरेटर क्या कर सकता है और यह क्या नहीं कर सकता है। यदि आप अपने खुद के लक्षण नहीं लिखते हैं, तो आपको इस टैग क्लास को std::iterator टेम्पलेट को तत्काल पर आपूर्ति करने की आवश्यकता है।

उदाहरण:

यह उदाहरण cplusplus से लिया जाता है।iterators पर कॉम अनुभाग:

class myiterator : public iterator<input_iterator_tag, int> 
{ 
    int* p; 
public: 
    myiterator(int* x) :p(x) {} 
    myiterator(const myiterator& mit) : p(mit.p) {} 
    myiterator& operator++() {++p;return *this;} 
    myiterator operator++(int) {myiterator tmp(*this); operator++(); return tmp;} 
    bool operator==(const myiterator& rhs) {return p==rhs.p;} 
    bool operator!=(const myiterator& rhs) {return p!=rhs.p;} 
    int& operator*() {return *p;} 
}; 

यह इटरेटर वास्तव में, मतलब नहीं है, क्योंकि यह केवल एक सूचक है, जो भी सीधे इस्तेमाल किया जा सकता गिर्द घूमती है। हालांकि यह एक स्पष्टीकरण के रूप में काम कर सकते हैं। इटेटरेटर उचित टैग की आपूर्ति करके से input_iterator के रूप में लिया गया है। इसके अलावा टेम्पलेट को बताया गया है कि यह इटेटरेटर int से अधिक है। अन्य सभी प्रकार, जिन्हें difference_type, reference, poiner इत्यादि की आवश्यकता है, स्वचालित रूप से टेम्पलेट द्वारा इनके द्वारा ऑफ़र किया जाता है। कुछ मामलों में यह इन प्रकारों में से कुछ को मैन्युअल रूप से बदलने का अर्थ हो सकता है (उदाहरण के लिए std::shared_ptr को कभी-कभी pointer के रूप में उपयोग किया जाना चाहिए)। इसके अलावा इस इटरेटर के लिए आवश्यक लक्षण स्वचालित रूप से मौजूद होंगे, क्योंकि यह पहले से ही std::iterator से लिया गया है और std::iterator_traits पता है कि सभी आवश्यक जानकारी कहां मिलें।

3

यहां इटेटरेटर है जो ट्रैवर्सल के दौरान उपन्यासों की गणना करता है। मैंने इसे विंडोज़ के लिए लिखा था, लेकिन मुझे लगता है कि इसे अन्य प्लेटफार्मों के लिए इसे कास्टोमाइज़ करना मुश्किल नहीं है।

#include <list> 
#include <windows.h> 
#include <assert.h> 
#include <iostream> 
#include <string> 

class File{}; 

class Iterator 
{ 
public: 
    virtual bool isDone() = 0; 
    virtual void next() = 0; 

    virtual std::string getFileName() = 0; 

    virtual ~Iterator(){}; 
}; 

bool operator== (Iterator& lhs, Iterator& rhs); 

class EndIterator : public Iterator 
{ 
public: 
    virtual bool isDone() {return true;} 
    virtual void next(){}; 
    virtual std::string getFileName() {return "end";}; 
}; 

class DirectoryIterator : public Iterator 
{ 
public: 
    DirectoryIterator(const std::string& path); 

    virtual bool isDone(); 
    virtual void next(); 

    virtual std::string getFileName(); 

    virtual ~DirectoryIterator(); 

private: 
    std::list<Iterator*> getSubelementsList(const std::string& path) const; 
    void init(); 

private: 
    bool m_wasInit; 
    std::string m_path; 
    std::list<Iterator*> m_leaves; 
    std::list<Iterator*>::iterator m_current; 
}; 

class FilesIterator : public Iterator 
{ 
public: 
    FilesIterator(const std::string& fileName); 

    virtual bool isDone(){return true;}; 
    virtual void next(){}; 

    virtual std::string getFileName(); 

    virtual ~FilesIterator(){}; 

private: 
    std::string m_fileName; 
}; 


class DbItertor 
{ 
public: 
    DbItertor(Iterator* iterator) : m_ptr(iterator){} 
    DbItertor(const DbItertor& rhs) {*m_ptr = *rhs.m_ptr;} 

    std::string operator*() 
    { 
    if(m_ptr->isDone()) 
     return "end"; 
    return m_ptr->getFileName(); 
    } 
    //File operator->(){return FileOpen(m_ptr->getFileName());} 

    void operator++() {m_ptr->next();} 

    ~DbItertor(){delete m_ptr;} 
private: 
    Iterator* m_ptr; 
}; 


class FileDB 
{ 
public: 
    FileDB(std::string dir) : m_rootDir(dir){} 


    DbItertor begin() 
    { 
    return DbItertor(new DirectoryIterator(m_rootDir)); 
    } 
    DbItertor end() 
    { 
    return DbItertor(new EndIterator()); 
    } 

private: 
    std::string m_rootDir; 
};  

FilesIterator::FilesIterator(const std::string& fileName) : 
    m_fileName(fileName) 
{} 


std::string FilesIterator::getFileName() 
{ 
    return m_fileName; 
} 

DirectoryIterator::DirectoryIterator(const std::string& path) : 
    m_wasInit(false), 
    m_path(path) 
{} 

void DirectoryIterator::init() 
{ 
    m_leaves = getSubelementsList(m_path); 
    m_current = m_leaves.begin(); 
    m_wasInit = true; 

    next(); 
} 

DirectoryIterator::~DirectoryIterator() 
{ 
    for(std::list<Iterator*>::iterator i = m_leaves.begin(); i != m_leaves.end(); ++i) 
    delete *i; 
} 

void DirectoryIterator::next() 
{ 
    if(!m_wasInit) 
    init(); 

    if(isDone()) 
    return; 

    if((*m_current)->isDone()) 
    ++m_current; 
    else 
    (*m_current)->next(); 
} 

bool DirectoryIterator::isDone() 
{ 
    if(!m_wasInit) 
    init(); 

    return (m_leaves.size() == 0) || (m_current == --m_leaves.end()); 
} 

std::string DirectoryIterator::getFileName() 
{ 
    if(!m_wasInit) 
    init(); 

    return (*m_current)->getFileName(); 
} 


std::list<Iterator*> DirectoryIterator::getSubelementsList(const std::string& path) const 
{ 
    std::list<Iterator*> result; 

    WIN32_FIND_DATA fdFile; 
    HANDLE hFind = NULL; 

    char sPath[2048] = {0}; 

    sprintf(sPath, "%s\\*.*", path.c_str()); 

    hFind = FindFirstFile(sPath, &fdFile); 
    assert(hFind != INVALID_HANDLE_VALUE); 

    do 
    { 
    if(strcmp(fdFile.cFileName, ".") != 0 && strcmp(fdFile.cFileName, "..") != 0) 
    { 
     std::string fullName = path; 
     fullName += std::string(fdFile.cFileName); 

     if(fdFile.dwFileAttributes &FILE_ATTRIBUTE_DIRECTORY) 
     { 
     fullName += "\\"; 
     result.push_back(new DirectoryIterator(fullName)); 
     } 
     else 
     { 
     result.push_back(new FilesIterator(fullName)); 
     } 
    } 
    } 
    while(FindNextFile(hFind, &fdFile)); 

    FindClose(hFind); 

    return result; 
} 

bool operator== (Iterator& lhs, Iterator& rhs) 
{ 
    return lhs.getFileName() == rhs.getFileName(); 
} 

bool operator!= (DbItertor& lhs, DbItertor& rhs) 
{ 
    return *lhs != *rhs; 
} 

int main() 
{ 
    FileDB db("C:\\456\\"); 
    for(DbItertor i = db.begin(); i != db.end(); ++i) 
    { 
    std::cout << *i << std::endl; 
    } 

    return 0; 
} 
संबंधित मुद्दे