directory iterator



  • hallo, ich will eine iterator-klasse schreiben, mit dem ich durch die einzelnen dateien in einem verzeichnis iterieren kann.
    (also grob gesagt alle dateinen in einem verzeichnis auslesen)
    nach ein wenig suchen in der doku bin ich auf die FindFirstFile und FindNextFile funktion gestoßen, mit denen sich das warscheinlich realisieren lässt:

    WIN32_FIND_DATA data;
       HANDLE hFile = FindFirstFile(dir.c_str(), &data);
       do{
            if(hFile == INVALID_HANDLE_VALUE){
    			cout << "ERROR" << std::endl;
    		}else{
    			std::wstring fname = data.cFileName;
    
        }
        while(FindNextFile(hFile, &data));
    

    das problem ist nun, dass wenn ich daraus einen iterator schreiben wollte, ich keinen 'operator --()' schreiben könnte, da die suche nur in eine richtung funktioniert. desweiteren kann ich zwar eine 'begin()' aber keine 'end()' schreiben, weil ich nicht weiß welches die letzte datei ist, bis ich dort angekommen bin.
    gibt es irgenteine andere mögichkeit etwas derartiges zu programmieren (bzw. informationen über dateinen in einem verzeichnis zu erhalten) oder gibt es doch irgentwelche schicken anderen funktionen in der winapi, die ich übersehen habe?


  • Mod

    Gibt es einen Grund, nicht Boost:Filesystem zu benutzen? Die haben auch einen fertigen Iterator.

    Der Trick, einen end-Iterator anzubieten, wenn man das Ende nicht kennt, ist übrigens, dass man dem end-Iterator irgendeinen besonderen Wert gibt ( = ein ungültiger Wert, der sonst niemals vorkommen könnte). Ein ++ auf einen Iterator, wodurch das Ende erreicht würde, führt dann zu diesem Wert. Denk dran, end() ist per Konvention nicht das Ende, sondern eins hinter dem Ende.



  • gamer8o4 schrieb:

    das problem ist nun, dass wenn ich daraus einen iterator schreiben wollte, ich keinen 'operator --()' schreiben könnte, da die suche nur in eine richtung funktioniert

    Da hast du eben nur ein Forward-Iterator.

    gamer8o4 schrieb:

    desweiteren kann ich zwar eine 'begin()' aber keine 'end()' schreiben, weil ich nicht weiß welches die letzte datei ist, bis ich dort angekommen bin.

    end() ist nicht ein Iterator auf die letzte Datei, sondern dahinter. Es lässt sich nicht dereferenzieren.

    gamer8o4 schrieb:

    gibt es irgenteine andere mögichkeit etwas derartiges zu programmieren (bzw. informationen über dateinen in einem verzeichnis zu erhalten)

    Boost.Filesystem.



  • Gibt es einen Grund, nicht Boost:Filesystem zu benutzen? Die haben auch einen fertigen Iterator.

    ja, mehrere 😉
    1. ich bin schüler und habe keinen druck, mein ziel ist es nicht ein fertige riesige programme zu schreiben, sondern besser programmieren zu lernen und deshalb nehme ich mir die zeit für sowas um zu verstehen wie es funktioniert.
    2. die boost-bibliotheken sind zwar super, aber wenn man nur eine klasse oder so für sehr kleine anwendungen daraus braucht finde ich es unnötig.

    Der Trick, einen end-Iterator anzubieten, wenn man das Ende nicht kennt, ist übrigens, dass man dem end-Iterator irgendeinen besonderen Wert gibt

    gute idee 🤡

    Ich habe übrigens schon in den code von boost geguckt, aber naja, da blick ich nicht durch, weil hunderte externe dateinen und funktionen verwendet werden und ja, ist mir zu kompliziert das zu versuchen zu verstehen...

    dann hast du eben nur einen forward-iterator

    hab ich auch erstmal nichts gegen, aber schöner wäre natürlich mit 😃


  • Mod

    gamer8o4 schrieb:

    Ich habe übrigens schon in den code von boost geguckt, aber naja, da blick ich nicht durch, weil hunderte externe dateinen und funktionen verwendet werden und ja, ist mir zu kompliziert das zu versuchen zu verstehen...

    Ja, solcher Code lässt sich praktisch nicht mehr lesen, höchstens mit ganz, ganz viel Geduld. Ebenso eine "echte" Implementierung der Standardbibliothek.

    Aber man kann sich an solchen Bibliotheken gut orientieren, was technisch möglich ist und wie das Design aussehen könnte. Wenn man sich sehr gut auskennt und einem mehrere Implementierungen einfallen, kann man auch ungefähr erkennen, welche Algorithmen benutzt wurden. Und wenn man wirklich Geduld hat, dann findet man auch den einen oder anderen guten Trick (ich war zum Beispiel schwer begeistert, wie elegant beim GCC die Wurzel des Rot-Schwarz-Baumes (Set & Co.) implementiert ist und habe mir das erst einmal selber für eigene Baumstrukturen abgeschaut. Da wäre ich nur mit wochenlangem Nachdenken selber drauf gekommen)



  • Ja, solcher Code lässt sich praktisch nicht mehr lesen, höchstens mit ganz, ganz viel Geduld.

    nebenbei, warum eigentlich nicht? warum werden solche bibliotheken so kryptisch geschrieben?
    Die meißten von euch arbeiten ja auch bei firmen als programmierer oder ähnliches, sieht der code den ihr dort programmiert auch so aus, oder wie? Würde mich echt mal interessieren 😉

    Aber man kann sich an solchen Bibliotheken gut orientieren, was technisch möglich ist und wie das Design aussehen könnte.

    mache ich gerne und oft, aber viel mehr kann ich da nicht rausholen, weil ich wie gesagt den code nicht verstehe. Bei Irrlicht, ogre und so kein problem, aber boost 😕



  • gamer8o4 schrieb:

    nebenbei, warum eigentlich nicht? warum werden solche bibliotheken so kryptisch geschrieben?

    Bei der Standardbibliothek ist mir vor allem aufgefallen, dass die Bezeichner sehr seltsam gewählt wurden (z.B. __M_next o.ä. als Member von vector ). Ich denke man nimmt hier reservierte Bezeichner für maximale Kompatibilität zum Nutzercode. Ein Bezeichner wie next könnte ja auch zufällig vom Nutzer als Makro definiert sein.

    Bei Boost ist vor allem das Problem, dass man möglichst kompatibel zu einer Vielzahl von Compilern und Umgebungen sein möchte und dadurch viel unnötiger Code für die verschiedenen Plattformen reinkommt.

    Vermutlich gibt es auch noch andere Gründe, aber die kenne ich nicht. Würde mich aber auch mal interessieren.



  • hola

    hab sowas aehnliches mal gemacht. ich probier das mal grob zu skizieren wie ich das gemacht habe:

    einen klasse forward die als klassenmember einen Handle (den du mit FindFirstFile erhaelst) enthaelt.
    2 Konstruktoren anlegen. ein std-ctor und einer der den pfad entgegen nimmt.
    wenn du nun einen iterator anlegst mit std-ctor dann ist es der end-iterator. der handle ist darin 0.
    der begin-iterator erhaelt den pfad ueber den konstruktor. darin wird FindFirstFile aufgerufen. das heisst der Handle != 0.
    bei jedem operator++([int]) wird FindNextFile aufgerufen.
    sobald FindNextFile 0 zurueckgibt, also keine weiteren dateien mehr vorhanden, schliesst du den Handle und setzt in auf 0.
    der operator!= wird als reiner vergleich der beiden Handles gemacht:

    bool file_iterator::operator!=(const file_iterator &it) const
    {
       return my_handle != it.my_handle;
    }
    

    nun kannst du es so schreiben:

    file_iterator begin("c:\\"), end;
    while(begin != end)
    {
       std::cout << begin(); // vorausgesetzt das du fuer file_iterator ueberladen hast
       ++begin;
    }
    

    hoffe es is verstaendlich geesen

    Meep Meep



  • also hier mal mein halb fertiger (aber funktionierender) code den ich gestern geschrieben habe:

    /* Copyright (c) Jannik Voss. All rights reserved. */
    
    #include "..\compileconfig.h"
    
    #ifndef DELTA_FILE_directory_iterator_H
    #define DELTA_FILE_directory_iterator_H
    
    #include <string>
    //#include <cstdio>
    #include <Windows.h>
    
    #include "..\core\include.h"
    #include "path.h"
    
    namespace delta{
    namespace file{
    
    	class directory_iterator{
    	public:
    		static directory_iterator end(){
    			directory_iterator ret;
    			ret.hfile = nullptr;
    			ret.current.clear();
    			return ret;
    		}
    
    	private:
    		// private standart constructor
    		directory_iterator(){
    			hfile = nullptr;
    		}
    
    	public:
    		// construction
    		explicit directory_iterator(const file::path& p){
    			if(p.is_file()) assert("delta::file::directory_iterator: invalid constructor-argument");
    
    			std::wstring dir = p.wstring();
    			dir += L'*';
    
    			hfile = FindFirstFile((LPCWSTR)dir.c_str(), &find_data);
    			if(hfile == INVALID_HANDLE_VALUE){
    				FindClose(hfile);
    				current.clear();
    			}else{
    				current.assign(find_data.cFileName);
    			}
    		}
    		directory_iterator(const directory_iterator& other){
    			*this = other;
    		}
    		~directory_iterator(){
    			if(hfile != NULL)
    				FindClose(hfile);
    			current.clear();
    		}
    	public:
    		// operators
    		directory_iterator& operator ++ (){
    			forward();
    			return *this;
    		}
    		directory_iterator operator ++(int) { 
    			directory_iterator tmp = *this;
    			forward();
    			return tmp;
    		}
    		directory_iterator& operator += (int num){
    			if(num < 0) assert("delta::file::directory_iterator: no backward traversing");
    
    			while(num--) forward();
    			return *this;
    		}
    		directory_iterator operator + (int num) const{ 
    			directory_iterator tmp = *this;
    			return tmp += num;
    		}
    
    		bool operator == (const directory_iterator& other) const{
    			return (current == other.current);
    		}
    
    		bool operator != (const directory_iterator& other) const{
    			return (current != other.current);
    		}
    
    		directory_iterator& operator = (const directory_iterator& other){
    			hfile = other.hfile;
    			find_data = other.find_data;
    			current = other.current;
    			return *this;
    		}
    
    	  #if defined (_MSC_VER) && (_MSC_VER < 1300)
    		#pragma warning(disable:4284) // infix notation problem when using iterator operator ->
    	  #endif
    
    		const file::path & operator * () { return current; }
    		const file::path * operator ->() { return &current; }
    
    	private:
    		// step forward to the next file
    		void forward(){
    			if(FindNextFile(hfile, &find_data)){
    				//if(hfile == INVALID_HANDLE_VALUE)
    				//	current.clear();
    				current.assign(find_data.cFileName);
    			}else{
    				FindClose(hfile);
    				current.clear();
    			}
    		}
    	private:
    		file::path current; // current filepath
    		WIN32_FIND_DATA find_data;
    		HANDLE hfile;
    	};
    
    };// ns: file
    };// ns: delta
    
    #endif DELTA_FILE_directory_iterator_H
    

    PS: delta::file::path ist eine klasse grob gesagt einfach eine klasse die einen dateipfad verwaltet. (ähnlich wie die path-klasse in boost)



  • gamer8o4 schrieb:

    also hier mal mein halb fertiger (aber funktionierender) code den ich gestern geschrieben habe:

    Hallo Jannik,

    'funktionieren' stimmt nur eingeschränkt. Mache mit Deiner Klasse mal folgendes:

    delta::file::path dir = ...;
        delta::file::directory_iterator i( path );
        {
            delta::file::directory_iterator copy_von_i( i );
            cout << "bis hier scheint alles gut ... " << endl;
        }
        for( ; i != delta::file::directory_iterator::end(); ++i )
            wcout << i->wstring() << endl;
    

    Du wirst sehen, dass das Programm nicht das erwartete Ergebnis erzielt, auch wenn path auf ein gültiges Directory verweist.

    Die Ursache liegt in dem Verstoß Deiner Implementierung gegen die Regel der drei (seit C++11 wohl 'der 5'). Bedenke, dass alle Kopien Deines Iterators auf das gleiche Directory wie der Original-Iterator zeigen (müssen). Falls eine der Kopien gelöscht wird, darf so lange nicht FindClose aufgerufen werden, solange noch einer der anderen Iteratoren existiert.
    Erst der letzte darf das Licht ausmachen - bzw. FindClose rufen.

    Sowas nennt man eine 'shared resource'. Und da das so oft vorkommt, muss man das nicht selber coden, denn es gibt in C++11 eine Klasse, die das kann. Das ist der std::shared_ptr<>. Dem (Smart-)Pointer kann man mitgeben, was er tun soll, wenn er bzw. die letzte seiner Kopien gelöscht wird. Hier wäre das die Funktion FindClose .

    Der Template-Parameter von std::shared_ptr bezeichnet das Objekt, auf das der Pointer zeigt. Der Pointer ist in diesem Fall das HANDLE. Jetzt kann man natürlich in den Tiefen der Windows-Header nach schauen, was das für'n Typ ist. Der Königsweg besteht darin, den Compiler den Typ automatisch bestimmen zu lassen. Ersetze also den Typ HANDLE in Deinem Iterator durch:

    #include <Windows.h>
    #include <memory> // shared_ptr
    #include <type_traits> // remove_pointer
    
    class directory_iterator
    {
    public:
        directory_iterator( const file::path& p )
            : find_data()
            , hfile( FindFirstFile( p.wstring().c_str(), &find_data ), &FindClose )
            , current( hfile.get() != INVALID_HANDLE_VALUE? find_data.cFileName: TEXT("") )
        {}
        // ...
    
    private:
        // .. Achtung Reihenfolge beachten
        WIN32_FIND_DATA find_data;
        std::shared_ptr< std::remove_pointer< HANDLE >::type > hfile;
        file::path current; // current filepath
    };
    

    Ich habe auch gleich die Initialisierungsliste verwendet und (wichtig!) die Reihenfolge der Member in die notwendige Reihenfolge hfile-benötigt-find_data current-benötigt-hfile gebracht.

    Der Kopier-konstruktor und der Zuweisungsoperator sind nun überflüssig, das erledigt der Compiler zusammen mit dem std::shared_ptr für Dich.

    Für die Implementierung eines Iterators fehlen noch die geforderten 'Traits'. Am einfachsten über typedefs zu implementieren:

    #include <iterator> // std::input_iterator_tag
    
    class directory_iterator
    {
    public:
        typedef std::input_iterator_tag iterator_category; // input- und single pass iterator
        typedef file::path value_type;
        typedef std::ptrdiff_t difference_type;
        typedef const file::path* pointer;
        typedef const file::path& reference; // const-iterator
    
        directory_iterator( const file::path& p )
        ...
    

    damit lässt sich der Iterator dann auch in Algorithmen wie std::distance oder std::advance benutzen. Letzteres wiederum macht in Deiner Implementierung die +-operatoren überflüssig.

    Gruß
    Werner



  • danke für die ausführliche antwort 👍

    Du wirst sehen, dass das Programm nicht das erwartete Ergebnis erzielt, auch wenn path auf ein gültiges Directory verweist.

    danke für den hinweis, das hätte ich warscheinlich erst in wochen gemerkt, wenn der fehler aufgetreten wäre.

    Für die Implementierung eines Iterators fehlen noch die geforderten 'Traits'. Am einfachsten über typedefs zu implementieren:

    oh ja, ich hab mich am design ein wenig von irrlicht inspirieren lassen und das ist dann wohl in die hose gegangen, da fehlen solche sachen nämlich.
    http://irrlicht.sourceforge.net/docu/irr_list_8h_source.html



  • gamer8o4 schrieb:

    Für die Implementierung eines Iterators fehlen noch die geforderten 'Traits'. Am einfachsten über typedefs zu implementieren:

    oh ja, ich hab mich am design ein wenig von irrlicht inspirieren lassen und das ist dann wohl in die hose gegangen, da fehlen solche sachen nämlich.
    http://irrlicht.sourceforge.net/docu/irr_list_8h_source.html

    .. da solltest Du Dich vielleicht lieber von boost oder noch besser vom C++-Standard inspirieren lassen. Anbei nochmal eine Iterator-Implementierung, die sich auf boost.iterator_facade abstützt. Dann schrumpft der Code auf das Wesentliche zusammen und es ist alles garantiert Standard-konform.

    #include <algorithm> // copy
    #include <memory> // shared_ptr
    #include <type_traits> // remove_pointer
    #include <iostream>
    #include <iterator> // std::input_iterator_tag
    #include <boost/iterator/iterator_facade.hpp>
    
    #include <Windows.h>
    
    class directory_iterator : public boost::iterator_facade< directory_iterator, file::path, boost::forward_traversal_tag, const file::path& >
    {
    public:
        directory_iterator( const file::path& p )
            : iterator_facade_()
            , find_data()
            , hfile( FindFirstFile( p.wstring().c_str(), &find_data ), &FindClose )
            , current( hfile.get() != INVALID_HANDLE_VALUE? find_data.cFileName: TEXT("") )
        {}
        directory_iterator() // end-iterator
            : iterator_facade_()
            , find_data()
            , hfile()
            , current()
        {}
    
        friend boost::iterator_core_access;
    
    private:
        void increment()
        {
            if( FindNextFile( hfile.get(), &find_data ) )
                current.assign( find_data.cFileName );
            else
                hfile.reset();
        }
        bool equal( const directory_iterator& b ) const
        {
            return !valid() && !b.valid(); // nur zwei Ende-Iteratoren sind gleich
        }
        iterator_facade_::reference dereference() const
        {
            return current;
        }
    
        bool valid() const
        {
            return hfile && hfile.get() != INVALID_HANDLE_VALUE;
        }
        // -- Member
        WIN32_FIND_DATA find_data;
        std::shared_ptr< std::remove_pointer< HANDLE >::type > hfile;
        file::path current; // current filepath
    };
    
    int main()
    {
        using namespace std;
        file::path dir( TEXT("C:\\*") );
        copy( directory_iterator( dir ), directory_iterator(), ostream_iterator< directory_iterator::value_type, wchar_t >( wcout, L"\n" ) );
        wcout << "\nDas Directory hat " << distance( directory_iterator( dir ), directory_iterator() ) << "Eintraege" << endl;
        return 0;
    }
    

    Gruß
    Werner



  • so, ich hab eure ratschläge mal versucht zu realisieren und die klasse überarbeitet.
    also ich finde sie sieht gleich viel schöner aus und die ersten test haben auch funktioniert 😉

    Bitte gebt mir eine kleine rückmeldung, ob ihr das so gemeint habt und ob ich so für den anfang zufrieden sein kann.

    boost::iterator_facade

    habe ich nicht genommen, weil wenn ich boost benutzen würde wer der sinn des selberschreibens weg und ich könnte gleich boost::filesystem::directory_iterator nehmen 🤡

    (nurnoch ~100 zeilen)

    #include "..\compileconfig.h"
    
    #ifndef DELTA_FILE_directory_iterator_H
    #define DELTA_FILE_directory_iterator_H
    
    #include <string>
    #include <memory>
    
    //#include <cstdio>
    #include <Windows.h>
    
    #include "..\core\include.h"
    #include "path.h"
    
    namespace delta{
    namespace file{
    
    	class directory_iterator{
    	public:
    		typedef std::input_iterator_tag iterator_category; // input- und single pass iterator 
    		typedef file::path value_type; 
    		typedef std::ptrdiff_t difference_type; 
    		typedef const file::path* pointer; 
    		typedef const file::path& reference; // const-iterator 
    
    		static directory_iterator end(){
    			directory_iterator ret;
    			ret.current.clear();
    			return ret;
    		}
    
    	public:
    		// construction
    		directory_iterator() // end-iterator   
    			: find_data() 
    			, hfile() 
    			, current()
    		{}
    		explicit directory_iterator(const file::path& p)
    			: hfile( FindFirstFile( (p.wstring()+L"*").c_str() , &find_data ), &FindClose )
    			, current( hfile.get() != INVALID_HANDLE_VALUE? find_data.cFileName: TEXT("") )
    		{}
    
    	public:
    		// operators
    		directory_iterator& operator ++ (){
    			forward();
    			return *this;
    		}
    		directory_iterator operator ++(int) { 
    			directory_iterator tmp = *this;
    			forward();
    			return tmp;
    		}
    		directory_iterator& operator += (int num){
    			if(num < 0) assert("delta::file::directory_iterator: no backward traversing");
    
    			while(num--) forward();
    			return *this;
    		}
    		directory_iterator operator + (int num) const{ 
    			directory_iterator tmp = *this;
    			return tmp += num;
    		}
    
    		bool operator == (const directory_iterator& other) const{
    			return (current == other.current);
    		}
    
    		bool operator != (const directory_iterator& other) const{
    			return (current != other.current);
    		}
    
    	  #if defined (_MSC_VER) && (_MSC_VER < 1300)
    		#pragma warning(disable:4284) // infix notation problem when using iterator operator ->
    	  #endif
    
    		const file::path & operator * () { return current; }
    		const file::path * operator ->() { return &current; }
    
    	private:
    		// step forward to the next file
    		void forward(){
    			if(FindNextFile(hfile.get(), &find_data))
    				current.assign(find_data.cFileName);
    			else
    				current.clear();
    		}
    
    	private:
    		typedef std::shared_ptr< std::remove_pointer< HANDLE >::type > shared_handle;
    
    		WIN32_FIND_DATA find_data; 
    		shared_handle hfile; 
    		file::path current; // current filepath 
    	};
    
    };// ns: file
    };// ns: delta
    
    #endif DELTA_FILE_directory_iterator_H
    


  • Hallo Jannik,

    Zeile 29 sollte überflüssig sein. Ich kenne die Implementierung von file::path zwar nicht, aber der Defaultkonstruktor sollte einen 'gültigen' leeren Pfad erzeugen. Dann wird aber auch die statische Methode end() überflüssig, es sei denn wegen 'syntactic sugar'.

    Ist nur formal, aber in Zeile 41 fehlt find_data in der Initialisierungsliste. Es gibt statisch Test (Lint), die das an-meckern würden.

    Die Operatoren operator+ und operator+= sind unnötig, da sie mit advance realisiert werden können. Sie implizieren dem Anwender lediglich, dass operator+ mit O(1)-Komplexität implementiert ist, was aber nicht der Fall ist (nicht sein kann). Das lädt nur zu einem Fehlgebrauch ein.
    Wegen so was hatte ich vor Jahren mal einen dicken Laufzeitfehler in ein Programm eingebaut - ich brauchte mehrere Test beim Kunden, um die Ursache zu ermitteln.

    operator*() und operator->() sollten und können const sein.

    Wenn zwei Iteratoren gleich sind, wenn der Filename gleich ist, so bedeutet dass, dass auch zwei Iteratoren aus unterschiedlichen Directorys gleich sein können. Den operator!= sollte man als !(*this == other) implementieren, um Redundanzen zu vermeiden.

    Gruß
    Werner



  • Hallo Jannik,

    oh, da war aber einer aufmerksam 🤡

    'syntactic sugar'

    cooler begriff 😃

    habe alle fehler bis auf einen ausgebessert 😉

    Die Operatoren operator+ und operator+= sind unnötig, da sie mit advance realisiert werden können. Sie implizieren dem Anwender lediglich, dass operator+ mit O(1)-Komplexität implementiert ist, was aber nicht der Fall ist (nicht sein kann). Das lädt nur zu einem Fehlgebrauch ein.
    Wegen so was hatte ich vor Jahren mal einen dicken Laufzeitfehler in ein Programm eingebaut - ich brauchte mehrere Test beim Kunden, um die Ursache zu ermitteln.

    verstehe das problem, aber da ich std::advance noch nie benutzt habe weiß ich nicht, wie ich es benutzen kann 😕



  • Hallo Jannik,

    gamer8o4 schrieb:

    verstehe das problem, aber da ich std::advance noch nie benutzt habe weiß ich nicht, wie ich es benutzen kann 😕

    Dafür hatte ich Dir in meinem Posting den Link angegeben. Da gibt es auch ein Beispiel.

    oder so:

    int main()
    {
        using namespace std;
        file::path dir( TEXT("C:\\*") );
        int anz = distance( directory_iterator( dir ), directory_iterator() );
        directory_iterator i( dir );
        advance( i, anz/2 ); // inkrementiere den Iterator bis zur Hälfte der Files
        copy( i, directory_iterator(), ostream_iterator< directory_iterator::value_type, wchar_t >( wcout << "Die zweite Haelfte:\n", L"\n" ) );
    
        return 0;
    }
    

    Gruß & Gute Nacht
    Werner



  • Werner Salomon schrieb:

    Sowas nennt man eine 'shared resource'. Und da das so oft vorkommt, muss man das nicht selber coden, denn es gibt in C++11 eine Klasse, die das kann. Das ist der std::shared_ptr<>. Dem (Smart-)Pointer kann man mitgeben, was er tun soll, wenn er bzw. die letzte seiner Kopien gelöscht wird. Hier wäre das die Funktion FindClose .

    Sowas nennt man Move-Only-Type. Man deaktiviert den Kopierkonstruktor/Zuweisungsoperator und schreibt dafür einen Move-Konstruktor und Move-Zuweisungsoperator. Wer die Ressource aus unerfindlichen Gründen sharen will (in deinem Beispiel würde eine Referenz auch genügen), muss das halt mit shared_ptr<directory_iterator> explizit hinschreiben.



  • in deinem Beispiel würde eine Referenz auch genügen

    ein sehr interessanter einfall!! 😮
    ich glaub das schreibe ich mal direkt um, ich mag die idee mit der referenz und es erscheint mir sicherer und schneller also... 🙂



  • Hallo gamer8o4,

    gamer8o4 schrieb:

    in deinem Beispiel würde eine Referenz auch genügen

    ein sehr interessanter einfall!! 😮 ... und es erscheint mir sicherer und schneller

    Ja - interessanter, schneller, verführerischer - quasi die dunkle Seite von C++.

    Ein directory_iterator ist zunächst einmal ein Iterator. Und ein Iterator muss - um dem C++ Standard zu genügen - 'CopyConstructible' sein. Steht ganz vorn bei dem Anforderungen an einen Iterator. Ist er das nicht, so kannst Du noch nicht mal einen std::copy damit aufrufen. Probiere es aus - mache den Kopierkonstruktor privat - Dein Compiler wird das gar nicht übersetzen!

    directory_iterator i = ....;
        copy( i, directory_iterator::end(), ostream_iterator<...>(...) );
    

    Praktisch alle Algorithmen verlangen das, wenn auch in dem einen oder anderen Fall der jeweiligen Implementierung ein moveable ausreichen würde. Und was bitte schön sollte man mit eine shared_ptr<Iterator> anfangen?

    Gruß
    Werner



  • also nicht mit referenzen machen? JA oder NEIN?


Anmelden zum Antworten