Array als Attribut einer Klasse [gelöst]
-
Hallo C++ Fans!

Ich bin gerade dabei ein Spiel zu schreiben...
Nun habe ich eine Klasse Level, die ich mir Ungefähr so vorgestellt habe:
class Level{ private: int map[][]; bool collision[][]; public: Level( int width, int hight, int map[width][hight], bool collision[width][hight] ); bool getCollision(int x, int y); void drawLevel(); ... };Ich weiß, dass das so nicht geht, ist eher Provisorisch.
Was ich erreichen Möchte, aber nicht hin bekomme ist folgendes:
Ich will ein 2D Array als Attribut haben, dessen Größe ich aber erst beim Aufruf des Objekt festlege (mit Konstruktor). Ist das Möglich?
Außerdem:
BeiLevel( int width, int hight, int map[width][hight], bool collision[width][hight] );sagt mir der Compiler:
Verwendung des Parameters »width« außerhalb des Funktionskörpers ...Komm mir grad ein bissl Unfähig vor... Ist es überhaupt Möglich, die Größe von Arrays erst beim Objektaufruf festzulegen. Bin in Arrays und Klassen noch ein wenig Wackelig...
Freu mich über Antworten und Vorschläge!
-
2d Arrays übergeben war immer schon lustig, vor allem zu C Zeiten.
C++ bietet die nette Möglichkeit, viele Dinge hinter Klassen zu verstecken.Genau das würde ich an deiner Stelle für die Map machen. Mach dir eine Klasse CMap2D, die dir die gleichen Möglichkeiten wie ein 2d Array bietet.
-
sdsdfs schrieb:
Mach dir eine Klasse CMap2D, die dir die gleichen Möglichkeiten wie ein 2d Array bietet.
Oder noch besser
Map2D:pIntern könntest du z.B. einen eindimensionalen
std::vectorhalten, dessen Grösse kann dynamisch gesetzt und verändert werden. Die Zugriffsfunktionen vonMap2drechnen dann Indizes von 2D nach 1D um.
-
Hi!
Erstmal danke für die Antwort...

Also: Ohne dass ich mir eine extra Map2D mache geht es also nicht ein 2D Array unbekannter größe zu übergeben?
ok...
Wie würde das mit dem Map2D ungefähr aussehen?
Also: Ich hab Map2D als Attribut meiner Klasse, während Map2D einen vector als Parameter hat, nicht? Und dieser wird dann in ein 2D Array Umgewandelt?
Und wie sprech ich dann die einzelnen Elemente an? Soll ich das mit einer Funktion machen wie: Map2D::getMap(x, y);
Oder kann ich eigentlich den [] Operator einbauen? Dass ich dann Map[x][y]; nur aufrufen muss?
-
jannis95 schrieb:
Also: Ohne dass ich mir eine extra Map2D mache geht es also nicht ein 2D Array unbekannter größe zu übergeben?
Boost.UBlas.Matrix?
jannis95 schrieb:
Und wie sprech ich dann die einzelnen Elemente an? Soll ich das mit einer Funktion machen wie: Map2D::getMap(x, y);
Oder kann ich eigentlich den [] Operator einbauen? Dass ich dann Map[x][y]; nur aufrufen muss?
Für den Index-Operator müsstest du dann ein Proxy-Objekt zurückgeben, dass wiederum den Index-Operator überladt. Also: nen einfachen Getter.
-
jannis95 schrieb:
Also: Ohne dass ich mir eine extra Map2D mache geht es also nicht ein 2D Array unbekannter größe zu übergeben?
Doch, sicher. Was du brauchst sind dynamische mehrdimensionale Arrays:
class Level{ private: int **map; bool **collision; public: Level(int width, int hight, int **m, bool **col); bool getCollision(int x, int y); void drawLevel(); ... }; Level::Level(int width, int hight, int **m, bool **col) { map = new int*[width]; for (int i=0; i<width; i++) { map[i] = new int[hight]; for (int j=0; j<hight; j++) map[i][j] = m[i][j]; } collision = new bool*[width]; for (int i=0; i<hight; i++) { collision[i] = new bool[hight]; for (int j=0; j<hight; j++) collision[i][j] = col[i][j]; } }Ein Array ist nichts weiter als ein Zeiger auf das erste Element. Im Falle von zweidimensionalen Arrays ist es ein Zeiger auf das erste Array, dass wiederum auf das erste Element zeigt.
Wie du aber sicher schon vermutest ist sowas fehleranfaellig.Du brauchst nichtmal eine eigene KLasse zu erstellen. Ich wuerde einfach einen mehrdimensionalen Vektor nehmen,
std::vector<std::vector<int> >bzw. koenntest du der Uebersicht halbertypedefs verwenden:
`typedef std::vector<std::vector<int> > Map2Dinttypedef std::vector<std::vector<bool> > Map2Dbool`
~(schade, dass man bei typedefs keine Templates benutzen kann ...)~
-
Vielen Dank erstmal!
Wieso ist das denn eigentlich Fehleranfällig? Welche Nachteile ergeben sich, wenn ich das jetzt einfach mit dynamische mehrdimensionale Arrays mache?
-
jannis95 schrieb:
Ich will ein 2D Array als Attribut haben, dessen Größe ich aber erst beim Aufruf des Objekt festlege (mit Konstruktor). Ist das Möglich?
Das ist in der Tat möglich. Die schöne Lösung wurde auch schon genannt:
Nexus schrieb:
Intern könntest du z.B. einen eindimensionalen
std::vectorhalten, dessen Grösse kann dynamisch gesetzt und verändert werden. Die Zugriffsfunktionen vonMap2drechnen dann Indizes von 2D nach 1D um.Ferris schrieb:
Ein Array ist nichts weiter als ein Zeiger auf das erste Element.
Nö. Ein Zeiger ist ein Zeiger. Ein Array ist ein Array.
-
template<typename T> class array_2d { private: vector<T> data; unsigned sy; unsigned sx; public: array_2d(unsigned y, unsigned x) : data(y*x), sy(y), sx(x) { } const T& operator()(unsigned y, unsigned x) const { return data[y*size_x() +x]; } T& operator()(unsigned y, unsigned x) { return data[y*size_x() +x]; } unsigned size_y() const { return sy; } unsigned size_x() const { return sx; } };Eine Instanz diese Klasse kannst du genauso benutzen, wie ein zweidimensionales Array, außer dass du statt
[zeile][spalte] (zeile,spalte)schreiben musst.
-
Ich versuch gerade die Variante mit den dynamischen Arrays, aber ich bekomme es nicht hin...
Der Konstruktor lautet ja so:
public: Level(int width, int hight, int **m, bool **col);Hab also versuch, ihn so aufzurufen:
#include "level.h" int main(int argc, char* args[]){ int mapL1[3][3] = { {0, 0, 0}, {0, 0, 0}, {0, 0, 0} }; int colL1[3][3] = { {0, 0, 0}, {0, 0, 0}, {0, 0, 0} }; Level l1(3, 3, mapL1, colL1); }Worauf mir der Compiler dann das hier sagt:
main.cpp: In Funktion »int main(int, char**)«: main.cpp:18:31: Fehler: keine passende Funktion für Aufruf von »Level::Level(int, int, int (*)[5][5], int [5])« main.cpp:18:31: Anmerkung: Kandidaten sind: level.h:11:9: Anmerkung: Level::Level(int, int, int**, bool**) level.h:11:9: Anmerkung: keine bekannte Umwandlung für Argument 3 von »int (*)[5][5]« nach »int**« level.h:5:7: Anmerkung: Level::Level(const Level&) level.h:5:7: Anmerkung: Kandidat erwartet 1 Argument, 4 angegebenWas mach ich jetzt wieder falsch?

-
Dynamische Arrays sind Arrays, die ihre größe in der Laufzeit fast beliebig ändern können. Also sieh dir mal die STL-Container an, besonders den std::vector.
-
jannis95 schrieb:
public: Level(int width, int hight, int **m, bool **col);Bei der Übergabe eines Arrays an eine Funktion zerfällt dieses in einen Zeiger auf das erste Element des Arrays. Das erste Element eines 2d Arrays ist ein Array der Größe X. Deine Funktion nimmt aber einen Zeiger auf einen Zeiger.
Ein Zeiger auf ein Array der Größe X != Ein Zeiger auf einen Zeiger.
Also:Level(int width, int hight, int (*m)[3], int (*col)[3])
-
out, wieso treibst du ihn noch dazu, C-Arrays weiterhin zu nutzen?
-
Sone schrieb:
out, wieso treibst du ihn noch dazu, C-Arrays weiterhin zu nutzen?
Wir können niemanden dazu zwingen, eine saubere Lösung zu verwenden. vector wurde schon des öfteren gennant, aber er wollte ja seine Lösung.
-
Es ist denke ich gar nicht so schlecht, sich als Anfaenger einwenig mit C-Arrays auseinanderzusetzen.
jannis95 schrieb:
Hab also versuch, ihn so aufzurufen:
#include "level.h" int main(int argc, char* args[]){ int mapL1[3][3] = { {0, 0, 0}, {0, 0, 0}, {0, 0, 0} }; int colL1[3][3] = { {0, 0, 0}, {0, 0, 0}, {0, 0, 0} }; Level l1(3, 3, mapL1, colL1); }Das Problem ist, dass statische Arrays nicht das gleiche sind wie dynamische Arrays. Bei eindimensionalen Arrays ist das ganze noch kein Problem, da du sie bei einer Funktionsuebergabe sowohl bei statischen als auch dynamischen Arrays einfach nur ein Zeiger entgegennehmen musst (der Zeiger zeigt dann auf das erste Element).
Bei mehrdimensionalen statischen Arrays ist die Groesse ab der zweiten Dimension aber fester Bestandteil des Typs.
Ein Arrayint arr[3][3]z.B. ist vom Typint (*arr)[3]also ein Zeiger auf das erste Array, dass drei Element besitzt.Um wieder auf deinen Level-Konstruktor zurueckzukommen, wenn du deine Arrays uebergeben willst, muesste der Prototyp so aussehen:
Level(int width, int hight, int (*m)[3], bool (*col)[3]);
Was dir aber nicht viel weiterbringt ...Wenn du unbedingt C-Arrays verwenden willst, waeren aber eventuell Templates die Loesung. Das bringt dir Vorteile:
-> Du kannst in der Klasse selbst einfach statische Arrays benutzen und musst nicht mit dynamischen Arrays hantieren. Die passende Groesse die du dann letztendlich brauchst wird dann beim Kompilieren bestimmt.
-> Dadurch dass du nun statische Arrays benutzen kannst, musst du dich nicht mehr um die Speicherverwaltung kuemmern, da statische Arrays auf dem Stack angelegt werden. Die Speicherverwaltung sind uebrigens auch der Grund, warum C-Arrays als "gefaehrlich" gelten.widthundheightwerden dann bei dem Klassentemplate keine direkten Attribute mehr sein - sondern wir verwenden sie praktisch direkt als "Datentyp".Hier ein Loesungsvorschlag:
template<size_t width, size_t height> class Level { private: int map[width][height]; int collision[width][height]; public: Level(int (*m)[height], int (*c)[height]); }; template<size_t width, size_t height> inline Level<width, height>::Level(int (*m)[height], int (*c)[height]) { for (size_t i=0; i<width; i++) { for (size_t j=0; j<height; j++) map[i][j] = m[i][j]; } for (size_t i=0; i<width; i++) { for (size_t j=0; j<height; j++) collision[i][j] = c[i][j]; } } ... // Spaeter im Code kannst du dann sowas machen: Level<5, 4> NeuesLevel(map, collision); // Konstante Variablen die zur Kompilierzeit feststehen, darfst du auch benutzen: const int width = 10, height = 8; Level<width, height> NeuesLevel(map, collision);Nachteil bei dieser Loesung ist, dass die Array-Groesse zur Kompilierzeit bestimmbar sein muss. Wenn du die Arrays Groesse aber erst zur Laufzeit (z.B. durch Eingaben des Benutzers) wissen kannst, dann muesstest du dynamische Arrays verwenden. Bzw. wenn du beides verwenden moechtest eventuell zwei Konstruktoren basteln. Einen Template Konstruktor, der statische Arrays nimmt und einen normalen der dynamische Arrays nimmt. Aber in diesem Fall kaemen C-Arrays sowieso nicht mehr in Frage.
out schrieb:
Ferris schrieb:
Ein Array ist nichts weiter als ein Zeiger auf das erste Element.
Nö. Ein Zeiger ist ein Zeiger. Ein Array ist ein Array.
Ein Array ist syntaktisch wie ein konstanter Zeiger auf das erste Element. Fertich!
-
zum heulen...
-
Ferris schrieb:
Ein Array ist syntaktisch wie ein konstanter Zeiger auf das erste Element. Fertich!
Mach camper nicht traurig!

-
Hi!
Es ist denke ich gar nicht so schlecht, sich als Anfaenger einwenig mit C-Arrays auseinanderzusetzen.
Ich wollte mich eigentlich mit beidem beschäftigen... Das Ziel meines Spieles ist ja, besser in C++ (und OpenGl) zu werden.
Nachteil bei dieser Loesung ist, dass die Array-Groesse zur Kompilierzeit bestimmbar sein muss. Wenn du die Arrays Groesse aber erst zur Laufzeit (z.B. durch Eingaben des Benutzers) wissen kannst, dann muesstest du dynamische Arrays verwenden.
Der Grund, warum ich dachte, Arrays währen der beste Weg hierfür war, dass ich ja eben nicht vorhabe meine Level Größe in der Laufzeit zu ändern. Ich will sie halt nur einmal beim Aufruf festlegen.
Werde es dann auch mit vector versuchen. Welche Lösung ich dann Schlussendlich nähme... KÖNNEN sollte ich ja beides

Vielen Dank für die Zahlreichen Antworten. Ich werde vielleicht noch schreiben, falls noch was unklar ist.
-
Du koenntest zur Uebung mal eine eigene einfache Vektor-Klasse mit Hilfe von einem Array schreiben. Ist gar nicht so schwer und man lernt sehr viel dabei (Arrays, Zeiger und Speicherverwaltung).
Werde es dann auch mit vector versuchen. Welche Lösung ich dann Schlussendlich nähme... KÖNNEN sollte ich ja beides
Ich wuerde dir aufjedenfall empfehlen das ganze trotzdem in eine extra Klasse zu packen. Wie du das dann intern machst (ob vector, Array oder was auch immer) wird nachher, wenn du die Klasse benutzt, keine Rolle mehr spielen. Das ist ja der Sinn und auch das schoene an OOP

Viel Erfolg bei deinem Projekt
-
Jap...
Noch mal kurz zu deinen Lösungsvorschlag...
Hab es gerade so versucht und es kommen ständig Fehler beim Compilieren..
Es kommt:
level.cpp:5:1: Fehler: Prototyp für »Level<width, height>::Level(int (*)[(height - 1)], int (*)[(height - 1)])« passt zu nichts in Klasse »Level<width, height>« level.h:8:3: Fehler: Kandidat ist: Level<width, height>::Level(int (*)[height], int (*)[height]) main.cpp: In Funktion »int main(int, char**)«: main.cpp:10:39: Fehler: keine passende Funktion für Aufruf von »Level<5, 4>::Level(int [3][3], int [3][3])« main.cpp:10:39: Anmerkung: Kandidaten sind: level.h:8:3: Anmerkung: Level<width, height>::Level(int (*)[height], int (*)[height]) [mit int width = 5, int height = 4] level.h:8:3: Anmerkung: keine bekannte Umwandlung für Argument 1 von »int [3][3]« nach »int (*)[4]« level.h:3:7: Anmerkung: Level<5, 4>::Level(const Level<5, 4>&) level.h:3:7: Anmerkung: Kandidat erwartet 1 Argument, 2 angegebenBitte sag mir schnell woran das liegt..
