Mehrdimensionale Arrays löschen



  • Mich wundert´s, warum es zu zweidimensionalen Arrays noch keine FAQ, gibt, vielleicht setz´ ich mich ja mal irgendwann hin und schreib´ eine. Die Frage nach zweidimensionalen Arrays ist wohl die häufigste, die hier gestellt wird.

    Wenn du die boost Bibliotheken verwenden kannst findest du dort die Klasse multi_array, die N-dimensionale Arrays implementiert.

    Falls du kein boost hast kannst du die Überlegung anstellen, dass sich jedes N-dimensionale Array linearisieren lässt, z.B. hat ein Array der Größe 4x2 8 Elemente, wobei die Elemente folgende Indizes besitzen:

    `

    +---+---+---+---+

    | 0 | 1 | 2 | 3 |

    +---+---+---+---+

    | 4 | 5 | 6 | 7 |

    +---+---+---+---+

    `

    Damit lässt sich jetzt einfach eine Klasse bauen, die ein 2-dimensionales Array implementiert:

    #include <vector>
    
    template<typename T>
    class array2D
    {
    public:
       array2D() :
          Rows_( 0 ),
          Cols_( 0 )
       {
       }
    
       array2D( unsigned int Rows, unsigned int Cols ) :
          Rows_( Rows ),
          Cols_( Cols ),
          Data_( Rows * Cols )
       {
       }
    
       unsigned int rows() const
       {
          // Anzahl der Zeilen zurückgeben
          return Rows_;
       }
    
       unsigned int cols() const
       {
          // Anzahl der Spalten zurückgeben
          return Cols_;
       }
    
       // const und non-const Zugriff auf Elemente im Array
       T& operator()( unsigned int Row, unsigned int Col )
       {
          // Zugriff über linearisierten Index
          return Data_[Row * cols() + Col];
       }
    
       const T& operator()( unsigned int Row, unsigned int Col ) const
       {
          // Zugriff über linearisierten Index
          return Data_[Row * cols() + Col];
       }
    
       void resize( unsigned int Rows, unsigned int Cols )
       {
          // !! TO DO !!
          // durch die Änderungen der Ausdehnung einer Dimension verschiebt
          // sich der lineare Index der Elemente, die resize() Methode braucht
          // etwas mehr Intelligenz. 
          Rows_ = Rows;
          Cols_ = Cols;
    
          Data_.resize( Rows * Cols );
       }
    
    private:
       unsigned int   Rows_;
       unsigned int   Cols_;
       std::vector<T> Data_;
    };
    

    Damit lässt sich dein Spielbrett dann so verwalten:

    int main()
    {
       array2D<Spielstein> Spielbrett( 8,8 ); // 8x8 Felder groß
    
       // Bsp.: Farbzuweisung an Spielstein
       Spielbrett( 0,0 ).Farbe = Color.Red;
    
       // Bsp.: Zuweisung an Array
       Spielstein NeuerStein;
       NeuerStein.Farbe = Color.Green;
    
       Spielbrett( 2,7 ) = NeuerStein;
    }
    

    Die array2D Klasse ist natürlich nur rudimentär implementiert, man kann sich noch nach Herzenslust austoben (zeilen- und spaltenorientierte Iteratoren, Stream Iteratoren, etc., pp.).



  • EDIT: Dieser Text bezieht sich nur auf die erste Antwort.

    Ich probiere nun die Initialisierung mit der 1. Möglichkeit.

    Also:

    static const int ZEILEN=7;
    static const int SPALTEN=6;
    
    Spielstein*** b=new Spielstein**[ZEILEN];
        for(int i=0;i<ZEILEN; i++) {
          b[i]=new Spielstein*[SPALTEN];
        }
    

    Fehlt da noch wo ein * in

    b[i]=new Spielstein*[SPALTEN]
    

    oder wieso wirds nicht richtig initialisiert?
    Fehler ist aber diesmal:

    Spielbrett.h:74: error: `new' cannot appear in a constant-expression                                              
    Spielbrett.h:74: error: ISO C++ forbids initialization of member 'b'                                              
    Spielbrett.h:74: error: making 'b' static                                                                         
    Spielbrett.h:74: error: invalid in-class initialization of static data member of non-integral type 'Spielstein***'
    

    Und weitere Folgefehler...

    --------

    Okay, ich werde es mal so probieren mit dem Vector. Und statt des Objektes kann ich auch die Adresse speichern, also den Typ Spielstein* angeben, richtig?



  • static const int ZEILEN=7;
    static const int SPALTEN=6;
    
    Spielstein** b=new Spielstein*[ZEILEN];
    
    for(int i=0;i<ZEILEN; i++) {
        b[i]=new Spielstein[SPALTEN];
    }
    

    Brauchst natürlich nen Standardkonstruktor für die Spielsteine...

    EDIT: Wie ist denn b definiert?


  • Mod

    Fabulus schrieb:

    Spielstein* brett[ZEILEN][SPALTEN];
    

    Hier ist ggf. ein delete für die einzelnen Spielsteine notwendig, falls diese per new erzeugt wurden und nicht anderweitig verwaltet werden. Das Array selbst ist ein automatisches Objekt (oder ein Member einer Klasse), ein delete darf diesbezgl. nicht erfolgen.

    Fabulus schrieb:

    static const int ZEILEN=7;
    static const int SPALTEN=6;
    
    Spielstein*** b=new Spielstein**[ZEILEN];
        for(int i=0;i<ZEILEN; i++) {
          b[i]=new Spielstein*[SPALTEN];
        }
    

    Fehlt da noch wo ein * in

    b[i]=new Spielstein*[SPALTEN]
    

    oder wieso wirds nicht richtig initialisiert?

    Der Fehlermeldung nach sieht das eher so aus, als ob du die Initialisierung direkt in der Klassendefinition durchführen willst.



  • Also ich habe jetzt erst einmal die Klasse array2D geschireben, wie es hier aufgefuehrt wurde.

    Aber es gibt hier jetzt bei der Erstellung dieses Arrays ein Problem:

    Ich erstelle mir:

    array2D<Spielstein*> bret( 6,7 );
    

    Und es kommt:

    Spielbrett.h:77: error: expected identifier before numeric constant
    Spielbrett.h:77: error: expected ',' or '...' before numeric constant
    

    Sofern ich schreibe (was auch im Endeffekt mein Ziel ist):

    array2D<Spielstein*> bret( ZEILEN,SPALTEN );
    

    kommt:

    Spielbrett.h:77: error: 'ZEILEN' is not a type                                                                    
    Spielbrett.h:77: error: 'SPALTEN' is not a type
    

    ZEILEN und SPALTEN sind static const int zwei Zeilen darüber erstellt.


  • Mod

    Bitte hinreichend vollständigen Code zeigen. Meine Kristallkugel ist schon vor Jahren zu Bruch gegangen.



  • Meine array2D.h sieht genauso aus, wie von DocShoe beschrieben.

    Ich habe mir ein testprogramm geschrieben, in dem ints in einem array dieser Art gespeichert werden. Das klappt soweit.
    Verwende ich aber das Array in meiner Spielbrett.h, kommt der eben beschriebene Fehler, auch wenn integer die Elemente sein sollen.

    Hier mal der Code:

    #ifndef SPIELBRETT_H
    #define    SPIELBRETT_H
    #include "Spielstein.h"
    #include "array2D.h"
    
    const int Z=6;
    const int S=7;
    
    class Spielbrett {
    
    public:
    
        Spielbrett() { 
          init(); 
        }
        ~Spielbrett() {
    
        } // destructor
    
        void init();
    
        Spielstein& getStein(const int& x, const int& y) const { return *brett[x][y]; }
        // Getter-Methoden
        const int& getZeilen() const { return ZEILEN; }
        const int& getSpalten() const {return SPALTEN; }
    
        // Umleitung des Ausgabeoperators
        std::ostream& outputToStream(std::ostream& ostr) const;
    
        int getNextZeile(const int& y) const; // Holt sich die naechste Zeile in Spalte y
        bool setzeStein(const int& y, Spielstein& stein); // Setzt Stein in Zeile y
    
    private:
        // Hoehe und Breite des Feldes festlegen
        static const int ZEILEN=Z;
        static const int SPALTEN=S;
    
    /* ZEILE 77 */    array2D<int> bret(6,7);
        Spielstein* brett[ZEILEN][SPALTEN];
    
    };
    

    Die Methoden beziehen sich alle noch auf das alte brett, was aber sonst funktioniert.



  • Wer ist denn überhaupt Besitzer des Spielsteins? Irgendwas muss ja verantwortlich sein für die Erzeugung und Zerstörung der Spielsteine, wenn du keine Zeiger, sondern die Spielstein Klasse als Template Parameter benutzt erledigt das array2D für dich.

    Edit:
    Du kannst das array nicht statisch initialisieren, wie es in Zeile 77 geschieht. Benutz´ stattdessen die Initialisierungsliste des Konstruktors:

    class Spielbrett
    {
       array2d<Spielstein> Spielsteine_;
    
    public:
       Spielbrett() : Spielsteine_( 6,7 )
       {
       }
    }
    

  • Mod

    Fabulus schrieb:

    Ich erstelle mir:

    array2D<Spielstein*> bret( 6,7 );
    

    Und es kommt:

    Spielbrett.h:77: error: expected identifier before numeric constant
    Spielbrett.h:77: error: expected ',' or '...' before numeric constant
    

    Für die Initialisierung ist der Konstruktor zuständig. Die Fehlermeldung besagt nur, dass der Compiler eine Typenliste erwartet, weil es sich nach der öffnenden Klammer dort nur um eine Funktionsdeklaration handeln kann.



  • Was meinst du mit "Wer ist Besitzer"? Ich gebe mal zur Ergänzung die Klasse Spielstein an (ist abstrakt, aber das sollte ja kein Problem sein).

    Das große Problem ist dass auch beispielsweise keine Integer-werte im Array speichern kann (so wie oben mal gepostet). Es kommt trotzdem dieser Fehler 😞

    class Spielstein {
    public:
        virtual ~Spielstein() {}
        virtual int getColor() const { return farbe; }
        virtual bool isSet() const=0;
    
        enum Color {grau, rot,blau,gelb};
    
    protected:
        Color farbe;
    };
    

    In einem normalen Array kann ich ja auch die Adressen speichern 😞

    Wenn ich übrigens in meiner Testdatei dieses Array mit <Spielstein*> erstelle, klappt alles wunderbar.



  • Hab meinen Post gerade editiert, da sollte die Lösung drinstehen.

    Der Besitzer der Spielsteine ist das Ding, das mit new die Spielsteine erzeugt und in das Spielbrett einfügt. Wenn du einen Zeigertyp als Template Parameter für array2D benutzt enthält das array2D Objekt nur Nullpointer.



  • Ok, ich habe jetzt den Vector soweit erstellt und allen Elementen eine passende Adresse ausgegeben.
    Muss ich jetzt dennoch dafür einen Destruktor schreiben, oder löscht der Compiler jetzt die Elemente von selber? Also ein Destruktor von der Klasse Spielbrett. Weitere Zeiger oder dynamische Speicher kommen nicht vor.

    Eine andere Frage zur Klasse vector. Soweit ich richtig gesucht habe, gibt es noch keine Methode, um den Inhalt eines Vectors in einen anderen zu kopieren.
    Also habe ich gedacht, ich schreibe mir so eine. Natürlich bin ich dort auf den Iterator gestoßen, der jedes Element einzeln durchgeht.

    Da meine Klasse in einem Headerfile steht, muss ich mit std::vector<T>::iterator it den Iterator erstellen.

    Dies klappt aber soweit nicht. Darf ich denn überhaupt doppelt scopen? Wie umgehe ich das Problem?

    Der Code ist ganz einfach bisher:

    void copy(array2D<T>& arr) {
        std::vector<T>::iterator it=Data_.begin(); // Erstelle iterator
        std::vector<T>::iterator it2=arr.Data_.begin(); // Erstelle zweiten Iterator
    
        /* Pruefe nach, ob Groesse ausreicht (TODO) */
    
        while(it!=Data_.end()) {
           *it2=*it;
            it++;
            it2++;
        }
    }
    

    Als Fehler kommt:

    array2D.h: error: expected ';' before 'it'
    array2D.h: error: 'it' was not declared in this scope
    array2D.h: error: expected ';' before 'it2'
    array2D.h: error: 'it2' was not declared in this scope
    

Anmelden zum Antworten