Dynamisches Array


  • Mod

    Gar nicht.



  • Echt? Ich mein mit "am ende zeigt m_data irgendwohin" das der zeiger schon noch auf einen Index von dem Array zeigt.


  • Mod

    Ja, echt.



  • vivess schrieb:

    1.) wieder an den anfang zeigt und

    Du nimmst einfach ne Kopie des Pointers und hantierst damit dann rum

    2.) der Zeiger auf den letzten index zeigt wo sich etwas befindet (also quasi das size(), nicht die kapazität).

    und loopst schlussendlich bis zum letzten Element des Arrays.



  • vivess schrieb:

    Echt?

    Echt.
    Das ist auch ein Grund, warum rohe C Zeiger nicht in C++ Programmiererhände gehören.
    Du musst dich dabei nämlich um alles selbst kümmern.
    Und das ist nichts für Anfänger.
    Und außerdem hast du mit mdata gar kein Array. Du hast einen Zeiger auf Lied. Und ein Zeiger ist kein Array und auch nicht umgekehrt.
    Du musst dir den Beginn sowieso merken, z.B. für deinen Destruktor. Wie soll delete[] denn funktionieren wenn es den Wert bei new nicht übergeben bekommt sondern irgendwas anderes?
    Rohe C Zeiger gehören nicht in C++ Programmiererhände, dafür gibts Referenzen, Smartpointer und für dein Problem Container"klassen".
    Und deswegen ist deine "Aufgaben"stellung sinnfrei und dein Lehrer der Inkompetenz überführt.



  • Wutz schrieb:

    vivess schrieb:

    Echt?

    Echt.
    Das ist auch ein Grund, warum rohe C Zeiger nicht in C++ Programmiererhände gehören.

    Ach? Spannend, dass du C++-Entwicklern absprichst, mit Zeigern zu hantieren. Bleib lieber im C-Forum.

    Du musst dich dabei nämlich um alles selbst kümmern.

    Stimmt.

    Und das ist nichts für Anfänger.

    Wir waren alle Anfänger und mussten das lernen. Daher ist das zum Lernen genau passend!

    Und außerdem hast du mit mdata gar kein Array. Du hast einen Zeiger auf Lied. Und ein Zeiger ist kein Array und auch nicht umgekehrt.

    Und jetzt muss man nur noch wissen, dass man mit x[N] auf das N-te Array-Element von x zugreift, egal ob x ein Array oder ein Zeiger auf das erste Array-Element ist. Und schon ist die Verbindung hergestellt.

    Rohe C Zeiger gehören nicht in C++ Programmiererhände, dafür gibts Referenzen, Smartpointer und für dein Problem Container"klassen".

    Die sind aber nicht immer geeignet. Es ist - auch in C++ - nichts an rohen Pointern verkehrt! Was oft verkehrt ist, sind nicht-smarte besitzende Pointer. Man kann aber auch erst einmal new+delete lernen, um danach zu erfahren, wie es oftmals besser ist. Man muss sich auch als C++-Entwickler mit Pointern auskennen.

    Und deswegen ist deine "Aufgaben"stellung sinnfrei und dein Lehrer der Inkompetenz überführt.

    Ich finde es nicht abwegig, zu Lernzwecken etwas vector-ähnliches selbst zu implementieren.



  • Wutz schrieb:

    Rohe C Zeiger gehören nicht in C++ Programmiererhände, dafür gibts Referenzen, Smartpointer und für dein Problem Container"klassen".

    Immer wieder dieselbe Soße, es hört einfach nicht auf.

    C-Zeiger sind ein fundamentaler Bestandteil von C++. Ohne C-Zeiger zu verstehen, kann man auch nur oberflächliches Verständnis für Smart Pointers erlangen. Inkompetent sind eher diejenigen, die dies nicht wissen (wollen).

    Da draußen sind Tausende von C++-Programmieren, die - aus welchen Gründen auch immer (Legacy Code, Code Policies, Low-Level- und Library-Programmierung ...) - C-Zeiger benutzen, und diese alle als inkompetent hinzustellen ist ein Frechheit 😡



  • Wir haben die gleiche aufgabe schon mit vektoren gelöst.

    Ich hätte da aber nun noch ein problem und zwar habe ich ja zwei klassen, einmal für meine musikdatenbank deren variablen und funktionen:

    enum genre {Pop,Rock,Klassik,HardRock};
    
    class Lied
    {
    public:
        std::string titel;
        std::string interpret;
        int erscheinungsjahr;
        double laenge;
        genre genre1;
    ..    
        void neuer_eintrag(Lied& liedobjekt);
    ..
    };
    

    und die funktionen/kontruktoren etc. von meinem DynArray:

    class DynArray
    {
    public:
    	DynArray(int newCapacity);
    	DestrukDynArray();
    
    	Lied& at(int index);
    ..
    	void push_back(Lied elem);
    ..
            Lied* m_data;
            int m_size;
    ..
    };
    

    Und dann hab ich zwei .cpp wo ich dann jeweils die funktionen für die zwei klassen programmiere. Nun hab ich aber das problem, das ich da wo ich die funktionen für die Klasse "Lied" programmieren will, irgendwie nicht so recht auf die funktionen und variablen von der Klasse DynArray zugreifen kann. Wenn ich z.b. ein Liedobjekt mit titel, interpret etc. anlege und etwas hineinschreibe, und dann das liedobjekt in die DynArray funktion push_back reinstecken will

    DynArray.push_back(lied1);
    

    bekomm ich die Fehlermeldung:

    error: expected unqualified-id before '.' token

    Wenn ich auf z.b. DynArray.m_size zugreifen will

    DynArray.m_size
    

    bekomm ich

    error: expected primary-expression before '.' token

    Weiß wer was ich da falsch mach? Ich hab in beiden .cpp dateien #include "dynarray.h" stehen (in dynarray.h hab ich beide klassen-definitionen stehen).


  • Mod

    Klassen sind keine Objekte, Objekte sind keine Klassen.

    Wenn du schreibst DynArray.push_back(foo), dann ist das ungefähr so, als würdest du versuchen

    int = 5;
    

    Da würdest du auch nicht erwarten, dass das klappt.

    Achte bei dem Design deiner Klassen auf strikte Trennung von Zuständigkeiten:
    + Ein Lied ist nur das, ein Lied. Es weiß nichts von Dynarrays oder Liedersammlungen.
    + Ein DynArray ist nur das, ein dynamisches Array. Es weiß nichts von Liedern oder Liedersammlungen.
    + Eine Liedersammlung ist nur das: Eine Liedersammlung. Sie besteht wahrscheinlich aus einem DynArray von Liedern und weiß was DynArrays und Lieder sind; aber sie macht nichts von dem, was Aufgabe eines Liedes oder eines DynArrays ist.

    Der zweite Punkt ist vielleicht etwas schwierig, wenn du noch keine Templates kennst. In dem Fall solltest du ein DynLiedArray machen, welches ein dynamisches Array von Liedern ist, aber nicht die Aufgaben einer Liedersammlung hat.



  • SeppJ schrieb:

    Klassen sind keine Objekte, Objekte sind keine Klassen.

    Diese Eingrenzung gilt für C++. Vernünftige OO-Sprachen haben diese Einschränkung nicht, da sind auch alle Klassen Objekte und auch so zu gebrauchen.


  • Mod

    Wutz schrieb:

    Vernünftige OO-Sprachen haben diese Einschränkung nicht

    Aber natürlich sind das keine vernünftigen Sprachen, weil sie OOP haben! 💡 Die einzig wahre Sprache ist C. Alles andere ist für nubs.



  • SeppJ schrieb:

    Klassen sind keine Objekte, Objekte sind keine Klassen.

    Wenn du schreibst DynArray.push_back(foo), dann ist das ungefähr so, als würdest du versuchen

    int = 5;
    

    Da würdest du auch nicht erwarten, dass das klappt.

    Achte bei dem Design deiner Klassen auf strikte Trennung von Zuständigkeiten:
    + Ein Lied ist nur das, ein Lied. Es weiß nichts von Dynarrays oder Liedersammlungen.
    + Ein DynArray ist nur das, ein dynamisches Array. Es weiß nichts von Liedern oder Liedersammlungen.
    + Eine Liedersammlung ist nur das: Eine Liedersammlung. Sie besteht wahrscheinlich aus einem DynArray von Liedern und weiß was DynArrays und Lieder sind; aber sie macht nichts von dem, was Aufgabe eines Liedes oder eines DynArrays ist.

    Der zweite Punkt ist vielleicht etwas schwierig, wenn du noch keine Templates kennst. In dem Fall solltest du ein DynLiedArray machen, welches ein dynamisches Array von Liedern ist, aber nicht die Aufgaben einer Liedersammlung hat.

    Hm ok, ich versteh jetzt nicht so ganz.

    Punkt 1 wäre dann ja im grunde einfach ein struct Lied, wo ich den Typ Lied definier.

    Punkt 2 wäre die Klasse DynArray mit deren kontruktoren/funktionen/variablen, und die Klasse ist vorgegeben von der aufgabenstellung. Gehe nicht davon aus das wir die verändern dürfen.

    Punkt 3 wäre dann eine Klasse liedersammlung mit deren funktionen. Aber wie schreib ich denn die klasse Liedersammlung, das ich dort funktionen von der klasse DynArray verwenden darf(bzw. aufrufen darf)? In der klasse liedersammlung hab ich ja z.b. die funktion "neuer eintrag hinzufügen" wo der benutzer dann halt titel, interpret, etc. eingibt, diese Eingaben werden in ein Objekt Lied gespeichert, und dieses Objekt will ich dann der funktion DynArray.push_back() zuschicken damit die das in das dynarray verwurstelt.


  • Mod

    Du scheinst immer noch nicht den Unterschied zwischen Klassen und Objekten derselben verstanden zu haben.

    Eine Klasse ist wie eine Lexikonbeschreibung eines Wortes. Im Lexikon wird beispielsweise erklärt, was ein 'Hund' ist, wie er aussieht, wie er sich verhält.

    Ein Objekt einer Klasse ist eine konkrete Realisierung einer solchen Beschreibung. Also ein richtiger, echter Hund.

    Du kannst die Beschreibung im Lexikonartikel nicht streicheln und sie wird nie bellen. Das kann nur ein echter Hund. Genausowenig kann du der Beschreibung eines DynArrays ein Lied hinzu fügen. Du brauchst eine konkrete Objektinstanz dafür. Deine Liedersammlung sollte eine Instanz von DynArray enthalten.



  • SeppJ schrieb:

    Deine Liedersammlung sollte eine Instanz von DynArray enthalten.

    Und wie mach ich das?

    DynArray.push_back() scheint also nur auf die Deklaration "zuzugreifen", aber nicht auf die definition?

    (nur um Missverständnisse zu vermeiden, push_back und die ganzen anderen Funktionen hab ich natürlich auch programmiert in einer eigenen .cpp

    void DynArray::push_back(Lied elem)
    {
    ..
    ..
    }
    

    )



  • Oder praktisch erklärt:

    class Foo
    { };
    
    Foo bar; // Foo ist die Klasse, bar ein Objekt vom Typ Klasse
    

    Das Wort "Klasse" hast du doch auch im echten Leben.
    Es gibt die Klasse der Säugetiere, du bist als Mensch ein Objekt der Klasse der Säugetiere.
    Oder die Klasse der geraden Zahlen, bei der 2,4,6,... Objekte wären.



  • Gehe nicht davon aus das wir die verändern dürfen.

    <anekdote> lustigerweise hängt genau das aber sehr stark von deinem lehrer ab. an meiner uni (damals) bei einer der typischen algorithmen-und-datenstrukturen-veranstaltungen haben teams von leuten verschiedene such-algorithmen zu implementieren gehabt (semesterprojekt), die dann gegeneinander angetreten sind (vereinfacht gesagt: gewonnen hat, wer am schnellsten ist). eine gruppe hatte allerdings die glorreiche idee, dass sie nur dann gewinnen kann, wenn sie einen anderen algorithmus als den in der aufgabenstellung vorgegebenen verwenden würde. war natürlich kein problem.
    das zeigt nämlich, dass sich diese gruppe bewusst war, was vor- und nachteile bestimmter algorithmen sind. </anekdote>

    was SeppJ wohl meinte (mit trennung von zuständigkeiten):

    class Lied
    {
    //...
       void neuer_eintrag (Lied& lied);
    };
    

    einem lied willst du ein neues lied hinzufügen? das macht schon auf deutsch keinen sinn, geschweige denn auf C++. genau genommen ergibt das in keiner einzigen programmiersprache sinn. es ist zwar zumindest grammatikalisch (syntaktisch) korrekt, aber un-sinnig.

    Aber wie schreib ich denn die klasse Liedersammlung, das ich dort funktionen von der klasse DynArray verwenden darf

    SeppJ hat dir die abstrakte erklärung geliefert; jetzt eine konkrete noch dazu:
    funktionen, die du als teil einer klasse definierst, haben einen versteckten parameter: den this -zeiger, der ihnen übergeben wird. dieser zeiger zeigt auf die aktuelle instanz der klasse. wenn du also eine funktion aus der klasse DynArray verwenden willst, musst du ihr einen this -zeiger mitübergeben. in einer solchen funktion (üblicherweise nennt man das in C++ elementfunktion) kannst du dann auf andere elemente der jeweiligen instanz zugreifen.

    class Lied {
      string name;
      void print_name();
    };
    
    //...
    
    void 
    Lied::print_name() {
       cout << name << endl;
       //ist äquivalent zu 
       cout << this->name << endl;
       //bloß - woher kommt "this" und worauf zeigt es?
       //antwort: auf eine konkrete instanz!
    }
    
    //... die konkrete instanz muss erst irgendwo geschaffen werden!
    Lied einLied; //so z.B.
    einLied.print_name(); //this zeigt jetzt auf "einLied"
    
    Lied* zweitesLied = new Lied; //oder so
    zweitesLied->print_name(); //this ist jezt zweitesLied
    einLied.print_name(); //hier ist this ein zeiger auf einLied
    
    //das ganze wird - vom compiler - so implementiert:
    //"behind the scenes"
    struct Lied {
       string name;
    };
    
    void print_name (Lied* this_) {
       cout << this_->name << endl;
    }
    
    //...
    Lied einLied;
    print_name(&einLied);
    
    Lied* zweitesLied = new Lied;
    print_name(zweitesLied);
    print_name(&einLied);
    

    wie ich schon ganz am anfang gesagt hab: stell dir einfach vor, du hast zwei DynArrays. wie würdest du dann die aufgabe lösen? stell dir deine klasse liedsammlung vor, die *zwei* DynArrays verwalten muss: eines namens guteLieder und eines namens schlechteLieder .

    class LiedSammlung {
    //...
      void neuer_eintrag (const Lied& lied);
    };
    //...
    
    void
    LiedSammlung::neuer_eintrag (const Lied& lied) {
       //if lied.popularitaet() < 50
       //zu den guten liedern hinzufügen
       //else
       //zu den schlechten liedern hinzufügen
    }
    

    PS. es wirkt, als würdest du dich wirklich und selbständig mit dem problem auseinandersetzen. solchen fragestellern wie dir hilft man hier gerne!



  • Ich geh an die Decke..

    Das mit dem this werd ich heut nicht mehr verstehen 😡

    Was SeppJ und Ethon wohl gemeint haben war hoffe ich irgendwie sowas in der art:

    class liedersammlung
    {
    public:
        void neuer_eintrag();
        int alle_eintraege_anzeigen();
        size_t details_von_eintrag_anzeigen();
        int eintrag_bearbeiten();
        int eintrag_loeschen();
        int menu();
    
        DynArray a;
    };
    

    Also das ich dann in den funktionen von liedersammlung DynArray-Funktionen mit z.b. a.push_back() aufrufen kann. Das funktioniert auch soweit, nur wie krieg ich dann ein DynArray gescheit angelegt. Irgendwie muss ich ja in dem einstiegspunkt in main schon ein DynArray anlegen. Das lustige ist ja das diese Kontruktoren was ganz eigenartiges zu sein scheinen. Ich kann einfach in der main Funktion

    DynArray(5);
    

    schreiben und der macht das einfach ohne vorher sowas schreiben zu müssen

    DynArray a;
    a.DynArray(5);
    

    Das ist ja ganz lustig, aber wenn ich dann in der Klasse liedersammlung meine Funktion neuer_eintrag() habe

    void liedersammlung::neuer_eintrag()
    {
        Lied lied1;
        cout<<"Gebe Titel, Interpret, Erscheinungsjahr, Laenge des Liedes und Genre          an!\nTitel: ";  
        cin>>lied1.titel;
        cout<<"Interpret: ";
        cin>>lied1.interpret;
        cout<<"Erscheinungsjahr: ";
        cin>>lied1.erscheinungsjahr;
        cout<<"Laenge des Liedes in der Form Minute.Sekunde: ";
        cin>>lied1.laenge;
    
        a.push_back(lied1);
    }
    

    Hat der ja nichts gescheites zum push-backen weil ich ja vorher kein Array angelegt habe und deswegen ja m_data und alles mumpitz sind. Die aufgabe treibt mich echt in den Wahnsinn. Weiß wer wie man hinbekommt? Ich hab aber irgendwie so das Gefühl so wie ich mir das jetzt zurecht denk ist es auch wieder alles falsch 🙂 :p



  • class liedersammlung
    {
    public:
        void neuer_eintrag(); 
        int alle_eintraege_anzeigen();
        size_t details_von_eintrag_anzeigen();
        int eintrag_bearbeiten(); 
        int eintrag_loeschen();
        int menu();
        // Daten sollten private sein!!!
    private:
        DynArray a;
    };
    

    Es ist üblich den Zugriff auf die Daten einer Klasse zu kapseln und nur über die Methoden der Klasse zu ermöglichen. Da du keinen Konstruktor deklariert hast, wird der Compiler einen Standardkonstruktor anlegen. Sobald du eine Instanz deiner Klasse erzeugst, wird damit auch ein DynArray a automatisch angelegt (innerhalb der Instanz).

    void liedersammlung::neuer_eintrag()
    {
        Lied lied1;
        cout<<"Gebe Titel, Interpret, Erscheinungsjahr, Laenge des Liedes und Genre          an!\nTitel: ";  
        cin>>lied1.titel;
        cout<<"Interpret: ";
        cin>>lied1.interpret;
        cout<<"Erscheinungsjahr: ";
        cin>>lied1.erscheinungsjahr;
        cout<<"Laenge des Liedes in der Form Minute.Sekunde: ";
        cin>>lied1.laenge;
    
        a.push_back(lied1); // a wurde vom Konstruktor erzeugt
    }
    
    void main()
    {
        liedersammlung lieder; // Aufruf des Standardkonstruktors von liedersammlung
    
        lieder.neuer_eintrag(); // Lied hinzufügen
    }
    

    Edit: Ich hatte was überlesen 😞



  • Hmm ok aber das bringt mir jetzt auch nicht so wirklich weiter glaub ich.

    Mein problem ist ja mittlerweile das wenn ich z.b. den Kontruktor

    DynArray::DynArray(int newCapacity)
    {
        m_data = new Lied[newCapacity];
        m_data_anfang = m_data;
        m_capacity = newCapacity;
        m_size=0;
    }
    

    in main mit

    DynArray a(5);
    

    aufrufe und dann ein Objekt Lied erstelle und irgendwas reinschreibe

    Lied lied1;
        lied1.titel="test";
        lied1.interpret="interpret";
        lied1.erscheinungsjahr=3849;
        lied1.laenge=5.3;
    

    Und dann steck ich das lied1 in meine push_back funktion rein

    void DynArray::push_back(Lied elem)
    {
        m_size+=1;
        if(m_size == m_capacity)
        {
            resize(2*capacity());
            *m_data=elem;
        }
        else 
            *m_data=elem;
        m_data+=1;          // m_data auf den nächsten index zeigen lassen
    }
    

    Und dann schreib ich in main

    cout<<a.m_data->titel;
    

    Was natürlich nichts ausgibt, weil ich in der push_back-Funktion nicht das m_data der Instanz 'a' verändert habe. Es würde funktionieren wenn ich von der Instanz a in der main das

    a.m_data
    

    als Zeiger dem push_back übergebe und der hantiert dann mit dem a.m_data herum. Aber das ist denke ich nicht Sinn der sache.

    Ich hab mal etwas gesucht und es gibt vllt irgendwie die Möglichkeit alle Dyn-Array variablen als static zu machen, was die wohl dann irgendwie global machen, aber das ist denke auch nicht gut.

    mein kernproblem ist nun einfach das ich es nicht hinkrieg das meine DynArray-funktionen wie z.b. das push_back oder auch der konstruktor mit den Variablen aus z.b. einer Instanz die ich in main aufrufe hantieren zu lassen.



  • Du brauchst kein DynArray in main.


Anmelden zum Antworten