Dynamisches Array



  • hallo,
    wir sollen die funktionen dieser klasse schreiben (aufs minimum reduziert damit Post nicht zu lang):

    #ifndef DYNARRAY_H
    #define	DYNARRAY_H
    
    class DynArray
    {
    public:
    	DynArray(int newCapacity); //Konstruktor
    ..
    ..
    private:
    	void resize(int newCapacity);
    	Lied* m_data;
    	int m_size;
    	int m_capacity;
    };
    
    #endif	/* DYNARRAY_H */
    

    was zum Schluss ein dynamisches array sein soll für eine Liederdatenbank, deswegen das

    Lied* m_data;
    

    Lied ist in dem fall ein struct mit Titel, Interpret, Erscheinungsjahr etc.

    und zwar komm ich da bei der funktion:

    void resize(int newCapacity);
    

    nicht weiter.

    Die soll laut aufgabenstellung:

    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
    Hinweis: überlegen Sie, ob ein Aufruf dieser Funktion Auswirkungen auf Attribute der Klasse hat.

    Im schritt 1 soll man ja ein neues "dynamisches" Array der größe newCapacity erzeugen. Die funktion die wir dafür benutzen sollen ist:

    DynArray(int newCapacity);
    

    Und die soll laut aufgabenstellung das machen:

    DynArray(int newCapacity) dient dazu, ein DynArray-Objekt mit einer bestimmten Kapazit‰t anzulegen:
    • Erzeugen eines neuen Arrays mit der Größe newCapacity
    • Die Attribute capacity und size sollen entsprechend gesetzt werden
     capacity ist immer gleich der Grˆˇe des Arrays, also die maximal mˆgliche Anzahl an Elementen
     size ist immer die Anzahl an tats‰chlichen Elementen, die das DynArray-Objekt enth‰lt
    • Ein auf diese Art angelegtes DynArray-Objekt soll leer sein im Sinne von es enthält keine Elemente

    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?

    weiß wer wie man das macht?
    mfg



  • frag dich mal, was du machen müsstest, wenn du in derin main -funktion zwei DynArray-objekte anlegen müsstest und worauf m_data dann zeigen würde.

    noch ein hinweis, diese funktion:

    DynArray::DynArray
    

    nennt man konstruktor.

    ich würde deine aufgabenstellung aber nicht so interpretieren, dass man unbedingt den konstruktor in resize aufrufen muss. ein konstruktor ist im übrigen auch keine funktion (man kann bzw soll ihn nicht "einfach so" aufrufen, sondern nur, wenn man ein neues objekt erzeugen will).



  • Ich hab es nun so gemacht das ich in der resize Funktion selber ein (größeres) DynArray erstelle ohne den kontruktor zu benutzen, dann alles vom alten DynArray ins neue kopiere, anschließend den speicher vom alten DynArray mit delete wieder freigebe, und m_data auf das neue DynArray zeigen lass.

    Wenn ich ein DynArray der größe zwei anlege, dürfte m_data ja auf das erste element des Arrays zeigen.

    Aber soweit bin ich noch nicht, denn ich krieg das mit den klassen und dem header nicht so richtig eingebunden. Im moment hab ich ja so eine header datei:

    #ifndef DYNARRAY_H
    #define	DYNARRAY_H
    
    enum genre {Pop,Rock,Klassik,HardRock};
    
    class Lied
    {
    public:
        string titel;
        string interpret;
    ..
        void neuer_eintrag_hinzufügen(Lied& liedobjekt);
    ..
    
    class DynArray
    {
    public:
    	DynArray(int newCapacity); //Konstruktor
    	~DynArray(); //Destruktor
    .. 
    	void resize(int newCapacity);
    	Lied* m_data;
    	int m_size;
    	int m_capacity;
    };
    
    #endif	/* DYNARRAY_H */
    

    Und dann hab ich in dem projekt noch drei .cpp dateien, einmal die main.cpp und einmal eine dynarray.cpp und eine lied.cpp (jeweils für die funktionen/kontruktoren der zwei Klassen).

    Die sehen vom prinzip so aus:
    main.cpp

    #include<iostream>
    #include<string>
    
    using namespace std;
    
    int main()
    {
        ..
    }
    

    dynarray.cpp

    #include "dynarray.h"
    
    using namespace std;
    
    DynArray::DynArray(int newCapacity)
    {
        m_data = new Lied[newCapacity];
        m_capacity = newCapacity;
        m_size=0;
    }
    ..
    ..
    

    und lied.cpp genauso (vom prinzip her).

    Aber wenn ich das dingens dann compilieren will, bekomm ich z.b. im dynarray.h die Fehlermeldung:
    "unable to resolve identifier string"
    Was mach ich dann da falsch?
    Irgendwie scheint der ja nicht zu wissen was string ist, aber ich hab doch in main.cpp string eingebunden. Wenn ich in der dynarray.h #include<string> schreibe krieg ich die fehlermeldung
    "error string does not name a type"

    Ist das erste mal das wir deklerationen und definitionen der klassen getrennt machen sollen. Davor haben wir immer alles in die main.cpp geklatscht. Weiß wer wie ich das richtig mach?



  • Schreibe std::string. Wo std::string benutzt wird muss vorher auch ein include stehen.



  • Ah ok jetzt funktionierts. Hab also in der header es so stehen

    #ifndef DYNARRAY_H
    #define DYNARRAY_H
    #include<string>
    
    enum genre {Pop,Rock,Klassik,HardRock};
    
    class Lied
    {
    public:
        std::string titel;
        std::string interpret;
    ..
        void neuer_eintrag_hinzufügen(Lied& liedobjekt);
    ..
    };
    

    Aber wieso muss ich das denn so schreiben? normalerweise reicht doch ein #include<string> , wozu ist denn das "std::"?



  • suchst du "namespace cpp"



  • Ich hätte da nochmal eine frage:
    Mal angenommen ich habe

    m_data = new Lied[newCapacity];
    

    so ein Array angelegt und füge ein paar Lieder hinzu und mach sonst irgendwelche Sachen und am Ende zeigt m_data irgendwohin. Wie krieg ich es hin das der zeiger
    1.) wieder an den anfang zeigt und
    2.) der Zeiger auf den letzten index zeigt wo sich etwas befindet (also quasi das size(), nicht die kapazität).

    Hätte da jemand ne idee?


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


Anmelden zum Antworten