Verschiedene Objekte mit wiederkehrender Arbeit initialisieren



  • Hallo ihr Lieben,

    ich zur Zeit folgendes Problem bzw. Vorhaben:
    Ich habe einen Datensatz in einer Datei vorliegen. Diesen möchte ich einlesen, mit den Daten Numerik betreiben und anschließend einen interpolierten Wert abfragen können.

    Dazu habe ich schon erfolgreich eine Struktur geschrieben, welche die Datei unter Angabe des Namens und der Anzahl der Zeilen öffnet, die ganze Numerik durchführt und anschließend über eine Funktion einen interpolierten Wert zurückgibt: structure.get_interpolated_value(double x)

    Jetzt ist es natürlich so, dass ich mehrere Dateien vorliegen habe.

    Mein erster Gedanke war eine List anzulegen und darin mittels push_back ein Objekt einzufügen. Über einen Iterator könnte ich dann auf die Funktion zugreifen.

    Es krankt nur daran, dass ich mir dann merken muss, welches Element der Liste über welchen Datensatz, d.h. Datei, initialisiert wurde.

    Und da dachte ich mir, dass es natürlich auch eleganter gehen muss. Ist das Schlüsselwort dazu map ? Wenn ich das richtig verstanden habe, dann kann ich darin meine Elemente über einen Schlüssel finden, der in diesem Fall gerade z.B. gerade der Dateiname sein könnte.

    Erscheint das sinnvoll oder gibt es noch bessere Möglichkeiten?

    Viele Grüße,
    -- Klaus.



  • Musst Du das denn mit jeder Datei/Datensatz mehrmals durchführen? Wenn nicht, kannst Du doch einfach eine Datei nach der anderen verarbeiten?!
    Ansonsten klingt das mit der map schon ganz gut, wenn Du über den Dateinamen zugreifen willst/musst.
    Anderenfalls (einfach sequentiell) geht natürlich auch ein sequentieller Container wie vector oder list.



  • Hallo Belli,

    Belli schrieb:

    Musst Du das denn mit jeder Datei/Datensatz mehrmals durchführen? Wenn nicht, kannst Du doch einfach eine Datei nach der anderen verarbeiten?!

    Das ist eine exzellente Rückfrage. Eigentlich muss ich nicht 'das Prozedere' mit abspeichern. Eigentlich reicht nach der Numerik eine kleine Struktur, welche lediglich die Ergebnisse der Rechnungen in den Arrays festhält und in der Lage ist einen Interpolierten Wert zurückzugeben! 🙂

    Belli schrieb:

    Ansonsten klingt das mit der map schon ganz gut, wenn Du über den Dateinamen zugreifen willst/musst. Anderenfalls (einfach sequentiell) geht natürlich auch ein sequentieller Container wie vector oder list.

    Mh.
    Ich wollte schon sagen, dass der Zugriff über einen Namen die weitere Programmierung erleichtern würde, weil ich dann später den Iterator anhand des Namens setzen kann.
    Aber ich glaube, wenn ich dann nachschauen muss, wie ich es genannt habe, kann ich auch gleich schauen das wievielte Element der Liste es ist, um den Iterator zu setzen. 😃
    Also würde es tatsächlich auch ein Vektor tun. 🙂

    Viele Grüße,
    -- Klaus.



  • So,
    ich habe mal ein Minimalbeispiel konstruiert und (natürlich / leider) knallt es beim Kompilieren. 😞

    // mb.cpp
    #include <iostream>
    #include <list>
    #include <vector>
    struct foo
    {
    foo(unsigned int const); 
    ~foo();
    double* const p_array;
    };
    
    foo::foo(unsigned int const n):
    p_array(new double [n])
    {}
    
    foo::~foo()
    {
    	delete [] p_array;
    }
    
    int main()
    {
    
    	unsigned int const n = 10;
    
    	foo f(n);
    
    	std::vector<foo> l_foo;
    
    	l_foo.push_back(foo(n));
    
    	std::cout << "Everything is fine ... now clean up!" << std::endl;
    
    	return 0;
    }
    

    Allerdings lässt sich das Programm kompilieren, wenn ich die Zeile auskommentiere, wo die Struktur in den Vektor geschoben wird.

    Als Antwort des Kompilers kriege ich:

    make: Warning: File `Makefile-Debug' has modification time 4,7e+02 s in the future
    g++ -g -Wall -pedantic -std=c++0x -c -ffast-math -O0 mb.cpp
    mb.cpp: In member function ‘foo& foo::operator=(const foo&)’:
    mb.cpp:6: instantiated from ‘void std::vector<_Tp, _Alloc>::_M_insert_aux(__gnu_cxx::__normal_iterator<typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer, std::vector<_Tp, _Alloc> >, _Args&& ...) [with _Args = foo, _Tp = foo, _Alloc = std::allocator<foo>]’
    /usr/include/c++/4.4/bits/vector.tcc💯 instantiated from ‘void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = foo, _Tp = foo, _Alloc = std::allocator<foo>]’
    /usr/include/c++/4.4/bits/stl_vector.h:747: instantiated from ‘void std::vector<_Tp, _Alloc>::push_back(_Tp&&) [with _Tp = foo, _Alloc = std::allocator<foo>]’
    mb.cpp:30: instantiated from here
    mb.cpp:6: error: non-static const member ‘double* const foo::p_array’, can't use default assignment operator
    In file included from /usr/include/c++/4.4/vector:69,
    from mb.cpp:4:
    /usr/include/c++/4.4/bits/vector.tcc: In member function ‘void std::vector<_Tp, _Alloc>::_M_insert_aux(__gnu_cxx::__normal_iterator<typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer, std::vector<_Tp, _Alloc> >, _Args&& ...) [with _Args = foo, _Tp = foo, _Alloc = std::allocator<foo>]’:
    /usr/include/c++/4.4/bits/vector.tcc:314: note: synthesized method ‘foo& foo::operator=(const foo&)’ first required here
    make: *** [mb.o] Fehler 1

    Scheinbar etwas mit dem Vergleichsoperator? Zunächst frage ich mich, was genau das Problem ist, da das Element ja nur in den Vektor geschoben werden soll.
    Aber da der Container die Option der Sortierung hat, hat der Vektor sicherlich keine Möglichkeit festzustellen nach was er eingefügten Elemente sortieren soll? 😕

    Gruß,
    -- Klaus.



  • Der Zuweisungsoperator '=' schlägt wohl fehl, weil Deine Zeigervariable const-deklariert ist; so würde ich das auf die Schnelle interpretieren.

    Edit:
    Mein VC++ compiliert es allerdings ohne Murren und warnt nur, weil kein Zuweisungsoperator generiert werden kann (wegen const vermute ich nach wie vor)



  • ...



  • So,
    nach Rücksprache mit einem Bekannten, habe ich das Problem vom Design ganz anders gelöst, weil ich die ganzen Funktionen von vector nicht brauche.

    Auch wenn ich wahrscheinlich viel über Ro3 gelernt hätte. 😉

    // mb.cpp
    #include <iostream>
    struct foo
    {
    foo(unsigned int const); 
    ~foo();
    unsigned int const n;
    double* const p_array;
    };
    
    foo::foo(unsigned int const x):
    n(x), p_array(new double [n])
    {
    	for(unsigned int i = 0; i < n; ++i)
    		p_array[i] = i;
    }
    
    foo::~foo()
    {
    	delete [] p_array;
    }
    
    int main()
    {
    
    	unsigned const n = 10; 
    
    	struct foo* a_foo[5];
    
    	a_foo[0] = new foo(n);
    
    	for(unsigned int i = 0; i < a_foo[0]->n; ++i)
    		std::cout << a_foo[0]->p_array[i] << std::endl;
    
        delete a_foo[0];
    
    	return 0;
    }
    

    Gruß,
    -- Klaus.



  • Warum hantierst du mit rohen Arrays? Die Dinger soltest du mal ganz schnell durch std::vector ersetzen.

    Ich hoffe, das Speicherloch hier ist in deinem echten Code nciht vorhadnen:

    a_foo[0] = new foo(n);
    


  • daddy_felix schrieb:

    Warum hantierst du mit rohen Arrays? Die Dinger soltest du mal ganz schnell durch std::vector ersetzen.

    Wie meinen? Wenn ich mit vector arbeite lande ich doch wieder direkt beim Ausgangsproblem.

    daddy_felix schrieb:

    Ich hoffe, das Speicherloch hier ist in deinem echten Code nciht vorhadnen:

    a_foo[0] = new foo(n);
    

    Guter Punkt! Habe ich gefixt. 🙂

    Gruß,
    -- Klaus.



  • Klaus82 schrieb:

    daddy_felix schrieb:

    Warum hantierst du mit rohen Arrays? Die Dinger soltest du mal ganz schnell durch std::vector ersetzen.

    Wie meinen? Wenn ich mit vector arbeite lande ich doch wieder direkt beim Ausgangsproblem.

    Du hast nicht wirklich begriffen, wo das Problem bei deinem Beispiel liegt?

    double* const p_array;
    

    Mach daraus einen std::vector. Dann lösen sich all deine Probleme in Luft auf. Du hast keine Konflikte mit der Rule of Three, das struct ist kopierbar und somit kannst du das struct in Containern verwalten und du musst die Größe des Arrays nicht immer separat mitschleppen.



  • Ich weiß nicht genau was ich nicht begriffen haben soll. Auf jeden Fall scheine ich mit dem folgenden Minimalbeispiel (siehe unten) genau den Kern meines Problems zu treffen:

    Ich initialisiere zunächst die Struktur foo , welche im Konstruktor zur Laufzeit einen array erzeugt.
    Diesen Array möchte ich bei der Initialisierung von bar an eben jene Struktur übergeben.

    Ich dachte, wenn ich kein & verwende, dann machte ich call-by-value, d.h. ich kopiere den Array aus foo nach bar , d.h. bar besitzt nun seine eigene Kopie. Also zeigt auf einen neu angeforderten Speicherbereich.

    Allerdings ist es wohl so, dass ich tatsächlich nur die Addresse übergebe. D.h. der Array in bar zeigt jetzt auch auf den Speicherbereich des Arrays in foo .

    Schließlich bekomme ich am Ende des Programms einen Speicherzugriffsfehler. Wenn (k.A. wer offiziell zuerst kommt) foo vernichtet wird und seinen Destruktor aufruft, dann gibt es den Speicher wieder frei, wohin der Array zeigt.
    Wenn dann bar seinen Destruktor aufruft, dann ist der Speicher allerdings schon freigegeben und somit gibt mir Valgrind einen Fehler aus:

    ==3105== Invalid free() / delete / delete[]
    ==3105==    at 0x4023503: operator delete[](void*) (vg_replace_malloc.c:409)
    ==3105==    by 0x804888D: foo::~foo() (mb.cpp:21)
    ==3105==    by 0x8048963: main (mb.cpp:59)
    ==3105==  Address 0x42b9028 is 0 bytes inside a block of size 80 free'd
    ==3105==    at 0x4023503: operator delete[](void*) (vg_replace_malloc.c:409)
    ==3105==    by 0x80488E5: bar::~bar() (mb.cpp:45)
    ==3105==    by 0x8048957: main (mb.cpp:59)
    

    Um das Problem zu umgehen habe ich (sicherlich umständlich und nicht ganz korrekt) eine Funktion in foo eingerichtet, welche tatsächlich die Addresse zurückgibt und anschließend den Pointer auf die Null setzt. Immerhin soll der Speicherbereich fortan bar gehören und foo soll bereit sein für neu zu erzeugende Arrays.

    double* const get_adress();
    
    double* const foo::get_adress()
    {
    	double* const tmp = p_array;
    	p_array = NULL;
    	return tmp;
    }
    

    Wodurch ich bar mittels

    struct bar b(f.get_adress());
    

    aufrufen kann und alles scheint in Butter! 🙂

    Vielen Dank fürs durchalten und lesen.

    Viele Grüße,
    -- Klaus.

    // mb.cpp
    #include <iostream>
    struct foo
    {
    foo(unsigned int const); 
    ~foo();
    unsigned int const n;
    double* p_array;
    };
    
    foo::foo(unsigned int const x):
    n(x), p_array(new double [n])
    {
    	for(unsigned int i = 0; i < n; ++i)
    		p_array[i] = i;
    }
    
    foo::~foo()
    {
    	delete [] p_array;
    }
    
    struct bar
    {
    	bar(double* const);
    	~bar();
    
    double* const p_array;
    };
    
    bar::bar(double* const p):
    p_array(p)
    {}
    
    bar::~bar()
    {
    	delete [] p_array;
    }
    
    int main()
    {
    
    	unsigned const n = 10; 
    
    	struct foo f(n);
    
    	struct bar b(f.p_array);
    
    	std::cout << "Time to clean up!" << std::endl;
    
    	return 0;
    }
    




  • Was mein Vorposter bereits verlinkt hat, will ich dir nun kurz erklären.

    Die Regel der Drei besagt:
    Wenn man einen der folgenden Drei Funktionen definiert: Destruktor, Zuweisungsoperator oder Kopierkonstruktor - sollte man alle Definieren.
    Denn höchstwahrscheinlich wird in diesen Funktionen etwas getan (bspw. Resourcen-Verwaltung), was in den anderen auch getan werden sollte. Da die vom Compiler generierten Default-Versionen dieser Funktionen eben einfach etwas falsches tun könnten, wie nicht den Pointee sondern den Pointer zu kopieren.

    Dein Beispiel hat die Macke, dass es wirklich unschön gelöst ist.
    Was du wirklich willst, ist nicht eine komische Funktion die etwas völlig unintuitives tut (ein Getter ist i.d.R. immer const -qualifiziert und verändert das Objekt nicht), sondern die Regel der großen Drei beachten:

    struct foo
    {
        foo(unsigned int const); 
        foo( foo const& );
    
        ~foo();
        foo& operator=(foo f);
    
        foo& swap( foo& ); /// Optional, verwende ich hier für das Copy&Swap-Idiom
    
        unsigned int const n;
        double* p_array;
    };
    

    Und die Definition sieht folgendermaßen aus:

    foo::foo( foo const& f ):
        n(f.n),
        p_array( new double[n] )
    {
        std::copy( f.p_array, f.p_array + n, p_array ); // Array rüberkopieren
    }
    
    foo& foo::operator=(foo f) /// Diese Art, den Zuweisungsoperator zu definieren nennt man das Copy&Swap-Idiom. Google es mal.
    {
        return swap(f);
    }
    
    foo& foo::swap( foo& f)
    {
        /// Hier werden nur die unterliegenden Skalare kopiert, nicht das Array o.ä.
        std::swap(f.n, n);
        std::swap(f.p_array, p_array);
        return *this;
    }
    

    Was du also tust ist, die korrekte Verwaltung der Ressourcen - kopieren, zerstören, usw. - gewissermaßen zu erzwingen, in dem du die entsprechenden Funktionen definierst. Dadurch kann gar nix mehr schiefgehen, rein theoretisch nicht.

    Also schön RAII beachten. 😋 👍

    Da du p_array einen ekligen Präfix ( p_ ) dranhängst, verweise ich dich auf Hungarian Notation.

    Code ist übrigens net getestet. ⚠



  • Sone schrieb:

    Code ist übrigens net getestet. ⚠

    eyyy gooooil, oldääär!
    sogar netzwerkgetesteten code gibbet hier!
    👍



  • Sone schrieb:

    struct foo
    {
        foo(unsigned int const); 
        foo( foo const& );
    
        ~foo();
        foo& operator=(foo f);
    
        foo& swap( foo& ); /// Optional, verwende ich hier für das Copy&Swap-Idiom
    
        unsigned int const n;
        double* p_array;
    };
    

    Und die Definition sieht folgendermaßen aus:

    foo::foo( foo const& f ):
        n(f.n),
        p_array( new double[n] )
    {
        std::copy( f.p_array, f.p_array + n, p_array ); // Array rüberkopieren
    }
    
    foo& foo::operator=(foo f) /// Diese Art, den Zuweisungsoperator zu definieren nennt man das Copy&Swap-Idiom. Google es mal.
    {
        return swap(f);
    }
    
    foo& foo::swap( foo& f)
    {
        /// Hier werden nur die unterliegenden Skalare kopiert, nicht das Array o.ä.
        std::swap(f.n, n);
        std::swap(f.p_array, p_array);
        return *this;
    }
    
    foo::~foo()
    {
        delete[] p_array;
    }
    


  • ...



  • Swordfish schrieb:

    template< typename T >
    class foo_t
    {
        std::size_t /* warum zum teufel const */ size;
        T * data;
    

    Warum nicht const?

    Swordfish schrieb:

    public:
            foo_t( std::size_t const size )
    

    Ich frag mich eher, was das const hier soll :p



  • ...



  • Swordfish schrieb:

    out schrieb:

    Swordfish schrieb:

    template< typename T >
    class foo_t
    {
        std::size_t /* warum zum teufel const */ size;
        T * data;
    

    Warum nicht const?

    Weils't dann gleich ein Array nehmen kannst. :p

    Verstehe ich gerade nicht. Wie meinst?

    (mal schauen obs morgen früh klarer wird 🤡 )



  • ...


Anmelden zum Antworten