dynamische arrays in klassen
-
Schönen guten Tag,
Meine Programmier Klausur steht vor der Tür und es gibt leider immernoch vieles, dass bei mir nicht klappt.
Wir haben als Probeklausur die Aufgabe ein Lottospiel zu implementieren.
An sich hat das auch geklappt. Als 2te Teilaufgabe soll allerdings die anzahl der Spieler und zahlen (Also z.b 3 aus 45) dynamisch gemacht werden.Zuerst wollte ich die Anzahl der Spieler als dynamische Objekt Array machen.
Also in etwa so:
Lotto*player=new Lotto[n];
Allerdings gab es da Probleme mit dem Konstruktor. Da ich nicht wusste wie und ob man irgendwie dyn Objekte mit einem allgemeinen (nicht standart) konstruktor initiieren kann.
Also hab ich doch lieber alles als Attribute gemacht. Und das sieht so aus:#ifndef LOTTO_H #define LOTTO_H #include <cstdlib> #include <iostream> using namespace std; class Lotto { private: int*tipp; int*player; int matches; public: Lotto(int,int); Lotto(const Lotto& orig); virtual ~Lotto(); void setzen(int, int); void ziehen(); void ergebnis()const; }; #endif /* LOTTO_H */
#include "Lotto.h" Lotto::Lotto(int zahl, int spieler) { int*tipp=new int[zahl]; for(int i=1;i<=zahl;i++) tipp[i]=NULL; int*player= new int[spieler]; for(int i=1;i<=spieler;i++) player[i]=NULL; } Lotto::Lotto(const Lotto& orig) { } Lotto::~Lotto() { } void Lotto::setzen(int a, int anzahl) { cout << "Bitte " << a << " Zahlen von 0-" << anzahl-1 << " ankreuzen: " << endl; for (int i = 1; i <= a ; i++) { cout << "Zahl " << i << ": "; cin >> player[i]; } } void Lotto::ziehen() { cout << "Zahlen der Ziehung: "<<endl; for(int i=1;i<=7;i++){ player[i]=rand()%21; cout << player[i] << " "; } cout << endl; } void Lotto::ergebnis()const { }
int n,m,pl; cout << "Wie viele Spieler nehmen Teil? " , cin >> pl, cout << endl; cout << "Mit wie vielen Zahlen soll gespielt werden (x aus y)? " <<endl; cout << "x: ", cin >> n, cout << "y: ", cin >> m, cout << endl; Lotto spieler(n,pl); Lotto ziehung(n,pl); int eingabe=1; cout << "-----------Menü-----------" <<endl; cout << endl; cout << "1 Tipzettel ausfüllen" <<endl; cout << "2 Zahlen ziehen" <<endl; cout << "3 Gewinner bekannt geben" <<endl; cout << "0 Beenden" <<endl; while(eingabe!=0){ cout << "Ihre eingabe: "; cin >> eingabe; cout << endl; switch (eingabe){ case 1: spieler.setzen(n,m); break; //..............................................
Das Problem ist, dass er in bei der Eingabe der ersten Zahl in der setzen() Methode plötzlich abbricht und mich aus dem Programm wirft.
Und ja, ich weiß, dass ich spieler und tipp besser als 2 dim Array machen sollte. Aber das ist mir erst hinterher eingefallen und solange nicht mal das hier klappt wollte ich davon noch die Finger lassen.
-
Cabooze schrieb:
Lotto::Lotto(int zahl, int spieler) { int*tipp=new int[zahl]; for(int i=1;i<=zahl;i++) // <<== tipp[i]=NULL; int*player= new int[spieler]; for(int i=1;i<=spieler;i++) // <<== player[i]=NULL; }
Arrays gehen in C und in C++ von
0
bisN-1
.Weiters sind dein
tipp
undplayer
lokale Variablen im Konstruktor. Um auf die Membervariablen zuzugreifen brauchst du hier keine Deklaration (int
):Lotto::Lotto( int zahl, int spieler ) { tipp = new int[ zahl ]; for( int i = 0; i < zahl; ++i ) tipp[ i ] = 0; player = new int[ spieler ]; for( int i = 0; i < spieler; ++i ) player[ i ] = 0; }
... und auch kein
NULL
, das ist (war. heute:nullptr
) für Pointer, nicht fürint
s.Du kannst dir die Schleifen aber auch ganz spaaren:
Lotto::Lotto( int zahl, int spieler ) { tipp = new int[ zahl ](); // value-initialization mit 0 player = new int[ spieler ](); // selbiges }
am besten noch ab damit in die Initialisierungsliste:
Lotto::Lotto( int zahl, int spieler ) : tipp { new int[ zahl ]() }, player { new int[ spieler ]() } {}
-
Okay änder ich mal.
Cool funktioniert jetzt
Danke.
Noch eine Frage:
Kann ich dynamisches array, dass mit int**player als 2 dim erstellt ist einfach so in eine methode übergeben? Das hat soweit ich weiß mit nicht dynamischen Arrays in c++ nicht so leicht funktioniert.Oder anders gefragt: ist es doch irgendwie möglich ein dynamisches Array von objekten zu erstellen? Und wenn ja, wie löse ich das mit dem Konstruktor?
-
Natürlich kann man einen
T*
(Sternchen nach belieben hinzufügen) an eine Funktion übergeben, genauso wie einT[N]
. Der Zeiger aufT
bleibt ein Zeiger aufT
und das ArrayT[N]
zerfällt in einen Zeiger aufT
.Cabooze schrieb:
Oder anders gefragt: ist es doch irgendwie möglich ein dynamisches Array von objekten zu erstellen?
Nimm einen std::vector<> wenn du das darfst.
-
Swordfish schrieb:
Natürlich kann man einen
T*
(Sternchen nach belieben hinzufügen) an eine Funktion übergeben, genauso wie einT[N]
. Der Zeiger aufT
bleibt ein Zeiger aufT
und das ArrayT[N]
zerfällt in einen Zeiger aufT
.Cabooze schrieb:
Oder anders gefragt: ist es doch irgendwie möglich ein dynamisches Array von objekten zu erstellen?
Nimm einen std::vector<> wenn du das darfst.
Nope. Darf ich leider nicht.
Also mit dem dynamischen Array hab ich es jetzt so versucht:
Lotto::Lotto(int zahl, int spieler) { pl_tip=new int*[spieler]; for(int i=0;i<spieler;i++) pl_tip=new int[zahl]; }
Also das ist der Konstruktor. Leider funktioniert das nicht.
}
-
Lotto::Lotto(int zahl, int spieler) { pl_tip=new int*[spieler]; for(int i=0;i<spieler;i++) pl_tip[i]=new int[zahl]; }
-
Swordfish schrieb:
Lotto::Lotto(int zahl, int spieler) { pl_tip=new int*[spieler]; for(int i=0;i<spieler;i++) pl_tip[i]=new int[zahl]; }
Ok danke. Wie greife ich nun noch darauf zu?
mit *pl_tip[i] müsste ich ja auf das innere Array zugreifen. Aber wie greife ich auf das äußere zu? Nur pl_tip[i] funktioniert leider nicht.
-
Sag mal, habt ihr kein Buch/Skriptum/sonstwas?
pl_tip[spielerindex][zahlindex]
-
Swordfish schrieb:
Sag mal, habt ihr kein Buch/Skriptum/sonstwas?
pl_tip[spielerindex][zahlindex]
Wir haben 2 dim dynamische Arrays nicht durchgenommen. Deswegen frage ich ja.
Und die Aufgabe verlangt deutlich dynamische Speicherverwaltung mit variabler anzahl an spielern und zahlen.
Ich wüsste nicht wie es ohne so ein Array gehen soll.So klappt dann soweit. Danke für deine Hilfe
-
Ich versuche jetzt gerade ein 2 dim dyn Array mit einem destructor zu löschen.
Im Konstruktor wird folgendes 2 dim Array erstellt:
bingo::bingo(int n, int tip) { spieler = new int*[n]; for (int i = 0; i < n; i++) spieler[i] = new int[tip]; }
Und mein destructor sieht folgendermaßen aus.
bingo::~bingo() { int n, tip; for(int i=0;i<n;i++) delete [] spieler[i]; delete [] spieler; }
Ich habe n und tip als Globale variablen deklariert in der Header File. Mit
extern int n, tip;
Allerdings funktioniert es nicht. N und Tip werden in der Main aufgerufen und eingegeben. Allerdings kommt für n im Destruktor immer 0 raus.
Woran liegt das?
-
Cabooze schrieb:
Allerdings funktioniert es nicht. N und Tip werden in der Main aufgerufen und eingegeben. Allerdings kommt für n im Destruktor immer 0 raus.
Woran liegt das?Weil du mit globalen Variablen und selbstgefrickelter dynamischer Speicherverwaltung so ziemlich alles an Antipattern angesammelt hast, was in eine Programm dieser Größe passt. Ich wette, du hast mindestens die Regel der großen Drei missachtet, sofern dein Lehrer überhaupt selbst jemals davon gehört haben sollte. Nachdem du diesen Fehler korrigiert hast, kann man weiter gucken.
Die nächste Baustelle sind die globalen Variablen, von denen man besser wissen sollte, wie die Regeln zur Namensauflösung sind, wenn man sie schon benutzt (und man sollte sie in aller Regel nicht nutzen, wenn es sich vermeiden lässt). Du scheinst nämlich nicht zu wissen, welcher Bezeichner in deinem Programm sich an welcher Stelle auf welches Objekt bezieht.Warum überhaupt diese komischen Doppelzeiger, wenn doch letztlich alle "Zeilen" der Datenstruktur gleich lang sind. Da kann man doch besser einen einzigen, zusammenhängenden Speicherblock der Größe (Anzahl Zeilen) * (Anzahl Spalten) nehmen und spart sich dadurch viel Aufwand, sowohl was die Programmierung angeht, als auch bezüglich der Effizienz des resultierenden Programms. Abgesehen davon ist dynamische Speicherverwaltung kein Teil einer Lottoklasse. Was auch immer eine Lottoklasse überhaupt genau tun soll. Aber es ist gewiss nicht Speicherverwaltung, denn wie viele echte Lotto hast du schon gesehen, die Speicher verwalten? Das gehört in eine eigene Klasse. Und an der absurden Frage zu den echten Lottos, die du schon gesehen hast, merkst du auch, dass diese Klasse vielleicht nicht so sonderlich gut designt ist, denn es ist nicht klar, was sie überhaupt darstellen soll.
-
Naja ob mein Professor von den Regeln der großen 3 gehört hat weiß ich nicht. Und ich auch nicht.
Die Aufgabe lautet, dass das Bingo Spiel variabel sein soll und wir mit dynamischer Speicherverwaltung arbeiten sollen. Das heißt n ist die Spieleranzahl und tip ist die Anzahl an möglichen Zahlen die im Spiel angekreuzt werden können. Es soll nämlich mit einer einstellbaren Spielermenge, einer einstellbaren Ankreuzmenge und einer einstellbaren Zahlenbereichsmenge gearbeitet werden. Und wir MÜSSEN es mit dynamischer Speicherverwaltung machen. Arrays und Vectoren akzeptiert der Prof nicht. Wie soll ich es sonst machen, wenn nicht mit einem 2 dimensionalen dynamischen Array?
Die Globale Variable hab ich erstmal wieder raus gemacht. N und Tip werden nur von der Main übergeben.
Naja ich denke ich probiere es mal mit 2 klassen.
-
Ich hab es jetzt mit 2 Klassen gemacht. Spieler und Bingo.
Jetzt hab ich ein Problem das komischerweise erst eben auftritt seit ich das Programm neu gestartet habe. Ich hab nichts dran verändert und vorhin hat es funktioniert!Das ist der Konstruktor der bingo klasse:
bingo::bingo(int playermnt,int count) { for(int i=0;i<playermnt;i++) player=new spieler(count); }
Und der Spieler Klasse:
spieler::spieler(int zahlen) { tippzettel=new int[zahlen]; match=0; }
Das Problem ist, dass sobald, player[1] aufgerufen wird und ich eine zahl eingeben will, das programm abbricht. Und ich kann einfach keinen Fehler finden.
void bingo::setzen(int playermnt, int count, int max){ for(int i=0;i<playermnt;i++){ cout << "Spieler " << i+1 << " bitte Zahl zwischen 0 und " << max-1 << " setzen" <<endl; player[i].settip(count); } }
void spieler::settip(int count){ for(int i=0;i<count;i++){ cout << "Zahl " << i+1 <<": ", cin >> tippzettel[i]; } }
Weiß jemand was da los ist?
-
new spieler(count) != new spieler[count]
Ganz grosser Unterschied.
Beinew int[zahlen]
haste es ja auch richtig gemacht.Wobei das nächste, etwas schwieriger zu lösende Problem sein wird, dass du mit
new spieler[count]
dann vermutlich nen Compiler-Fehler bekommen wirst.BTW: Wieso mischt du Deutsch (spieler) und Englisch (player)?
-
hustbaer schrieb:
new spieler(count) != new spieler[count]
BTW: Wieso mischt du Deutsch (spieler) und Englisch (player)?Einfahc so. Hab da nicht so drauf geachtet.
Das hab ich absichtlich so gemacht mit der Klammer. Ich hatte im Internet gelesen dass man das so macht. Oder so in der Art.
Der Punkt ist, dass ich doch irgendwie den Konstruktor der Spielerklasse aufrufen muss. Und das mit jedem Spieler-Objekt, dass ich neu erstelle.
Wenn ich schreibe:
new spieler[count]dann erstellt er mir zwar ein Array aus Spielern, aber ich kann ja dann nicht den allgemeinen Konstruktor aufrufen.
Ich habe vorhin eben nochmal meinen Prof gefragt. Weil er unbedingt dynamische Speicherverwaltung will.
Ich hab es vorher mit einem 2 dim Dyn Array gemacht. Hat zwar funtkioniert, war aber etwas umständlicher. Da hat er mir empfohlen 2 Klassen zu erstellen. Aber jetzt hab ich wirklich keine Ahnung, wie das gehen soll. Die Anzahl Spieler und die Anzahl der Tippzettel soll variabel sein. Ich weiß beim besten Willen nicht wie er sich das vorstellt. Schon gar nicht mit dem, was wir bisher behandelt haben.
-
Ja, das war das was ich mit
Wobei das nächste, etwas schwieriger zu lösende Problem sein wird, dass du mit new spieler[count] dann vermutlich nen Compiler-Fehler bekommen wirst.
gemeint habe.
Wie du es machen kannst: du kannst ein (dynamisch angefordertes!) Array aus Zeigern auf
spieler
machen, und dann die Spieler einzeln der Reihe nach konstruieren.
Dann musst du beim Zusammenräumen natürlich auch erst alle Spieler einzeln löschen und dann zum Schluss nochmal das Array aus Zeigern.Etwas umständlicher, aber geht.
(Wenn nicht so viel verboten wäre könnte man natürlich
std::vector
mitemplace_back()
verwenden, und sich damit nen Haufen Murks sparen. Aber SO schlimm ist es ja auch nicht - gewöhn dir bloss nicht an so nen Mist zu machen wenn du es nicht so machen musst.)ps: Zweite Variante: verwende 2-Phase Construction. Ist im Allgemeinen verpönt, hier aber einfacher als die "Array aus Zeigern" Variante. D.h. du machst in dem (einzigen, parameterlosen) Konstruktor nix ausser brav alle Variablen mit "ungefährlichen" Werten zu initialisieren. Und machst dann eine eigene "Init" Funktion, an die du dann die Spieliernummer übergibst. Und rufst halt nach dem Erzeugen des Arrays die Init Funktion für jeden Spieler auf.
-
Oder placement-new:
#include <cstddef> #include <type_traits> #include <memory> #include <iostream> class player_t { private: std::size_t const num_numbers; unsigned * numbers; public: player_t( std::size_t num_numbers ) : num_numbers{ num_numbers }, numbers{ new unsigned[ num_numbers ]() } {} player_t( player_t & ) = delete; ~player_t() { delete [] numbers; } }; class bingo_t { private: typedef std::aligned_storage< sizeof player_t, std::alignment_of< player_t >::value >::type player_storage_t; std::size_t const num_players; player_t * players; public: bingo_t( std::size_t num_players, std::size_t num_numbers ) : num_players{ num_players }, players{ reinterpret_cast< player_t * >( new player_storage_t[ num_players ] ) } { for( std::size_t i = 0; i < num_players; ++i ) new ( players + i ) player_t( num_numbers ); } bingo_t( bingo_t & ) = delete; ~bingo_t() { for( size_t i = 0; i < num_players; ++i ) players[ i ].~player_t(); delete [] reinterpret_cast< player_storage_t * >( players ); } }; int main() { bingo_t bingo( 10, 5 ); }
-
Swordfish schrieb:
class player_t { private: std::size_t const num_numbers; unsigned * numbers; public: player_t( std::size_t num_numbers ) : num_numbers{ num_numbers }, numbers{ new unsigned[ num_numbers ]() } {} player_t( player_t & ) = delete; ~player_t() { delete [] numbers; } };
Auch du hast jede Menge Anti-Pattern angesammelt (const Member, falsche Reihenfolge der Member, private nach class, Kopierkonstruktor mit non-const-Referenz, Ro4 verletzt, _t für Typen, Braced initialisierung, etc.)
-
auch du mein sohn schrieb:
const Member, falsche Reihenfolge der Member, private nach class [...], _t für Typen, Braced initialisierung, etc.
mhm. passt schon.
auch du mein sohn schrieb:
Kopierkonstruktor mit non-const-Referenz, Ro4 verletzt
Aha? Was ist denn möglich was nicht sein dürfte?
-
Swordfish schrieb:
auch du mein sohn schrieb:
Kopierkonstruktor mit non-const-Referenz, Ro4 verletzt
Aha? Was ist denn möglich was nicht sein dürfte?
Wie viele Zeilen musst du ändern, um einen Move-Konstruktor hinzuzufügen?