Dynamisches Array


  • 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.



  • Na, aber irgendwo muss ich ja ein dynarray anlegen.

    aber ich gebs jetzt auch auf. Geht vllt. einfach nicht mit den Sachen die ich bis jetzt kann. drecks aufgabe, schmorr in der hölle.



  • vivess schrieb:

    Na, aber irgendwo muss ich ja ein dynarray anlegen.

    Ja, in Liedersammlung

    aber ich gebs jetzt auch auf. Geht vllt. einfach nicht mit den Sachen die ich bis jetzt kann. drecks aufgabe, schmorr in der hölle.

    Du hast etwas grundlegendes nicht verstanden. Das hat nichts mit dieser Aufgabe zu tun. Solange du das nicht verstehst wirst du ziemlich Probleme mit deinen weiteren Aufgaben haben.



  • aber in den folien steht auch ein feuchter dreck dazu. kann ja nicht sein das ich mir da alles selber ausm Hintern ziehen muss.



  • Was unterscheidet Dynarray von std::string?
    Was unterscheidet "Titel" von "Array von Liedern"?

    Wenn du Titel nicht in main erzeugen musst, warum dann das Array?



  • das war ja nur ein beispiel um mein problem darzustellen. selbst wenn ich dynarray in liedersammlung erzeuge hilft mir das genau GAR NICHTS.



  • vivess schrieb:

    das war ja nur ein beispiel um mein problem darzustellen. selbst wenn ich dynarray in liedersammlung erzeuge hilft mir das genau GAR NICHTS.

    Doch, hilft.

    Mal ein Beispiel:

    class Foo
    {
        public:
            void setIntValue(int i); // öffentliche Methonde, um den privaten Wert zu ändern
            int getIntValue(); // öffentliche Methode, um den privaten Wert abzurufen
        private:
            int theValue; // der privat gespeicherte Wert
    }
    
    void Foo::setIntValue(int i)
    {
        theValue = i;
    }
    
    int Foo::getIntValue()
    {
        return theValue;
    }
    
    void main()
    {
        Foo foo; // Erzeugt eine Instanz von Foo. Der vom Compiler automatisch erstellte
                 // Konstruktor wird nun alle intern benötigten Variablen erzeugen.
                 // In diesem Fall wurde "theValue" erzeugt.
        foo.setIntValue(10); // weißt dem privaten theValue einen Wert zu
    
        int aValue = foo.getIntValue(); // ruft diesen Wert wieder ab.
    }
    

    Wenn du keinen Konstruktor für deine Klasse vorgibst, dann wird vom Compiler einer automatisch erzeugt und dieser kümmert sich erst mal um alles nötige. Siehst du oben in "main" irgendwo, dass "theValue" explizit erzeugt wurde?

    Möglicherweise habe ich aber auch dein Problem nicht verstanden...

    Edit:

    Vielleicht haben wir dich allerdings auch unnötig verwirrt. Für die Aufgabe würde es tatsächlich reichen zwei Klassen zu erstellen. Eine Klasse "Lied" und eine Klasse "DynArray" (das Lieder aufnehmen kann). Dann würde es in der main ungefähr so aussehen (Pseudocode):

    DynArray liedersammlung{10}; dynamisches Array für 10 Lieder
    
    Lied lied;
    lied.titel = "Blah...";
    //usw.
    
    liedersammlung.push_back(lied);
    

    Allerdings hat "Neuer Eintrag" nichts in der Klasse Lied zu suchen. Ist ja logisch. Die Klasse Lied weiß ja nichts von dem Array, wie soll es sich also irgendwo neu eintragen können?



  • vivess schrieb:

    in main mit

    DynArray a(5);
    

    aufrufe und dann ein Objekt Lied erstelle und irgendwas reinschreibe

    Und dann steck ich das lied1 in meine push_back funktion rein

    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

    Wenn du

    a.push_back(...)
    

    dann wird natürlich auch "m_data" der Instanz "a" verwendet. Das ist ja der Sinn davon, die Daten gehören zur jeweilingen Instanz der Klasse.

    DynArray a(5);
    DynArray b(5);
    
    a.push_back(...); // verwendet das interne m_data von a
    b.push_back(...); // verwendet das interne m_data von b
    


  • vivess schrieb:

    2.3.5 void resize(int newCapacity)
    Ein Aufruf von resize vergrößert das Array eines DynArray-Objekts. Diese Funktion wird nur intern verwendet und ist
    daher private. Um ein bestehendes Array zu vergrößern m¸ssen Sie folgende Schritte durchführen:
    1. Erzeugen eines neuen dynamischen Arrays der Größe newCapacity
    2. Kopieren der Daten aus dem bestehenden Array in das neu erzeugte
    3. Löschen des bestehenden Arrays
    4. Den m_data-Zeiger auf das neu erzeugte Array setzen

    Ich versteh aber nicht wie ich das machen soll. Meine DynArray(int new Capacity) funktion sieht so aus:

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

    Aber wenn ich wie in der aufgabenstellung für das resize im Schritt 1 ein neues array anlege, wird ja der zeiger m_data direkt auf das neue array gerichtet, aber im 4. Schritt steht dann auf einmal ich soll den m_data zeiger auf das neu erstellte array richten, aber das mach ich doch schon sofort?

    Was ist mit Schritt 2? Um etwas kopieren zu können, muss zumindest bis die Kopie erstellt ist noch das Original vorhanden sein (m_data darf also nicht sofort neu zugewiesen werden, sonst ist es weg). Du musst genau das tun, was oben in den Schritten 1 - 3 beschrieben ist. "Resize" wird demnach so ähnlich arbeiten wie der Konstruktor, aber mit ein paar zusätzlichen Schritten.

    Außerdem noch:

    cout<<a.m_data->titel;
    

    Wenn a ein Array ist dann fehlt beim Zugriff auf die Daten irgendwie noch die Angabe eines Indexes. Woher soll dein Array wissen, welches Lied du genau haben möchtest, z.B.

    cout<<a.getLied(0)->titel;
    

    Ich lerne ja auch gerade C++ und wenn ich Probleme an einer Stelle habe, dann versuche ich erst mal mit einem vereinfachten Beispiel das Problem zu lösen. Anschließend erweitere ich die Lösung auf das ursprüngliche Problem.

    Bastel dir doch erst mal ein dynamisches Array für einfache Datentypen:

    class IntArray
    {
        public:
            IntArray(int capacity);
            ~IntArray(); // destructor nicht vergessen, um das interne array wieder freizugeben!
            void pushBack(int value);
            int get(int index);
        private:
            int* array = nullptr;
            int size = 0;
            int capacity = 0;
            void resize(int newCapacity);
    };
    

    Wenn das funktioniert:

    int main(int argc, char *argv[])
    {
        IntArray ints(10); // startgröße 10
    
        for (int i = 1; i <= 100; i++) // 100 werte einfügen
        {
            ints.pushBack(i);
        }
    
        std::cout << "the answer to all: " << ints.get(41) << std::endl; // ganz wichtig ;)
        return 0;
    }
    

    dann kannst du den Datentypen ja auf Lieder ändern.

    Schau dir auch im Debugger Schritt für Schritt an, was dein Programm macht oder lass dir zur Kontrolle in den einzelnen Methoden etwas auf die Konsole ausgeben:

    void IntArray::pushBack(int value)
    {
        std::cout << "pushBack begin, capacity: " << capacity << " size: " << size << std::endl;
        if (capacity <= size)
        {
            resize(capacity + 10); // resize array +10
        }
    
        array[size++] = value;
    
        std::cout << "pushBack done, capacity: " << capacity << " size: " << size << std::endl;
    }
    

    ...
    pushBack begin, capacity: 10 size: 8
    pushBack done, capacity: 10 size: 9
    pushBack begin, capacity: 10 size: 9
    pushBack done, capacity: 10 size: 10
    pushBack begin, capacity: 10 size: 10
    resize done, capacity: 20 size: 10
    pushBack done, capacity: 20 size: 11
    pushBack begin, capacity: 20 size: 11
    pushBack done, capacity: 20 size: 12
    pushBack begin, capacity: 20 size: 12
    pushBack done, capacity: 20 size: 13
    pushBack begin, capacity: 20 size: 13
    pushBack done, capacity: 20 size: 14
    ...


Anmelden zum Antworten