Objektkoordinaten 2d Array
-
Ja, klar.
Aber ich meinte mit "Structs" C-Style Structs und mit "Klassen" C++ Klassen/Structs.
War ungünstig ausgedrückt, ja.Ich sollte mir aber mal mein Passwort merken, dass ist schlimm.

-
Ja du hast recht Furble ich kauf mir nächten monat ein buch

WAs genau nicht funktionert ist das wenn ich nullptr verwende mir der compiler sagt das nullptr nicht deklariert ist. Wenn ich 0 benutze geht es aber ich weiss jetzt nicht wie sich das verhält ist 0 = nullptr?
das hero vor der funktion ist glaube ich die typbezeichnung für den rückgabewert.
sowas wie
int funktion(int a){ a=a+10; return a;}hero ist ja mein eigener typ und weil ich den in die funktion reingebe oder ausgeben will muss vor der funktion hero stehen oder?
Ich hoffe ich liege jetzt nicht total daben.Nein ich weiss nicht so genau wie es sich mit referenz und wert verhält. Das
fällt unter das thema zeiger oder?
Werde mich mal informieren.Über Klassen auch nochmal ich verstehe nicht alles aus deinem code. Warum print_value void sein kann ist mir jetzt klar. Aber der showdown oder wie du ihn nennst shutdown ? Ich will doch da mit hero red und blue rechnen muss ich die dann nicht beide reingeben?
-
Photon schrieb:
WAs genau nicht funktionert ist das wenn ich nullptr verwende mir der compiler sagt das nullptr nicht deklariert ist. Wenn ich 0 benutze geht es aber ich weiss jetzt nicht wie sich das verhält ist 0 = nullptr?
Jein.
Aber bei dir ist das egal.das hero vor der funktion ist glaube ich die typbezeichnung für den rückgabewert.
Ja
hero ist ja mein eigener typ und weil ich den in die funktion reingebe oder ausgeben will muss vor der funktion hero stehen oder?
Ich hoffe ich liege jetzt nicht total daben.Das vor dem Funktionsnamen ist der Rückgabewert, die Argumente stehen in den Klammern.
Du musst das nur davor schreiben, wenn du hero zurückgeben willst.[quote}Über Klassen auch nochmal ich verstehe nicht alles aus deinem code. Warum print_value void sein kann ist mir jetzt klar. Aber der showdown oder wie du ihn nennst shutdown ? Ich will doch da mit hero red und blue rechnen muss ich die dann nicht beide reingeben?[/quote]
Das shutdown bei mir war ein Tippfehler.
Und die musst du nicht beide reingeben, bei Membefunktion kannst du immer implizit auf die aktuelle Instanz zugreifen.
Bei:red.print_value()ist die aktuelle Instanz red und du kannst auf die Variablen von red zugreifen.
Bei
blue.print_value();
blue und due kannst darauf zugreifen.
Aber warte einfach auf dein Buch.
-
Photon schrieb:
Nein ich weiss nicht so genau wie es sich mit referenz und wert verhält.
Ich hab da mal was vorbereitet. Auch wenn meine didaktischen Konzepte in diesem Thread schon kritisiert wurden.

Folgendes:
#include <iostream> #include <string> // Held mit Name struct hero { std::string name; int dmg, def, hp; }; bool is_alive(hero h) { bool alive = 0<h.hp; if(alive) std::cout << h.name << " is alive.\n"; else std::cout << h.name << " is dead.\n"; return alive; } // Uebergabe per Wert void break_neck(hero h){ // h ist eine Kopie des übergebenen Objekts std::cout << h.name << " slipped and broke his neck!\n"; h.hp=0; } // Uebergabe per Referenz void meet_dragon(hero& h){ // h ist ein Alias für das übergebene Objekt std::cout << h.name << " took a wrong turn and entered the dragon's lair.\n"; h.hp=0; } int main(){ hero red = { "red", 10, 10, 10 }; hero blue = { "blue", 8, 8, 8 }; break_neck(red); if(is_alive(red)) std::cout << "Impossibru!\n"; meet_dragon(blue); if(is_alive(blue)) std::cout << "goddammit!\n"; }Das spannende sind Zeile 27:
h wird als eine neue Variable vom Typ hero deklariert - das System initialisiert h beim Aufruf mit dem Funktionsargument. h ist also eine Kopie(!) des Arguments.Und im Gegensatz dazu Zeile 33:
h wird als Referenz auf ein hero Objekt (hero&) deklariert. h ist lediglich ein Aliasname für das Funktionsargument. Referenzen erkennst Du am & hinter der Typangabe.Lass das Programm laufen. Hast Du die Ausgabe erwartet?
Wenn ja: Übergabe per Wert/Referenz begriffen.
Wenn nein: nochmal drüber grübeln - ist ganz einfach. Ebenso einfach wie der Unterschied zwischen einer Kopie und einem Alias im echten Leben.Memberfunktionen habe ich bisher bewusst nicht erwähnt.
-
ok ich war das wochende weg aber ich hab mir die sache angeschaut. Ich habe jetzt verstanden wie das mit den referenzen funktioniert und warum es besser ist direkt auf den speicherplatz zuzugreifen anstatt immer neue variablen zu erzeugen und diese auch im speicher abzulegen.
Als nächstes werde ich mir wohl die pointer nochmal genauer ansehen die ja auch was damit zu tun haben. Hab da noch nicht so ganz den durchblick.
Danke furble das war ein gutes beispiel. Jetzt werde ich öfter mal void funktionen nutzen können.
-
Photon schrieb:
ok ich war das wochende weg aber ich hab mir die sache angeschaut. Ich habe jetzt verstanden wie das mit den referenzen funktioniert und warum es besser ist direkt auf den speicherplatz zuzugreifen anstatt immer neue variablen zu erzeugen und diese auch im speicher abzulegen.
Zieh nicht die falschen Schlußfolgerungen. Besser oder schlechter habe ich noch gar nicht gesagt.
Was ich zeigen wollte:
In einer Funktion auf einer Kopie eines Arguments zu arbeiten ist etwas komplett anderes als auf dem Original zu arbeiten.Photon schrieb:
Als nächstes werde ich mir wohl die pointer nochmal genauer ansehen die ja auch was damit zu tun haben. Hab da noch nicht so ganz den durchblick.
Zeiger sind mit Sicherheit ein wichtiger Aspekt von C++, aber als Anfänger kommst Du erstmal gut ohne aus.
Z.B. möchte ich Dir noch zwei Sachen zeigen.
Mein letztes Beispiel enthielt zwei Verzweigungen, in denen ich Codefehler aufspüren wollte:
break_neck(red); if(is_alive(red)) std::cout << "Impossibru!\n"; meet_dragon(blue); if(is_alive(blue)) std::cout << "goddammit!\n";Nachdem rot sich den Hals brach, bzw. blau einen Drachen traf hätten beide tot sein sollen. Sobald eine der beiden Bedingungen wahr wurde war ich einem Programmfehler auf der Spur - in dem Fall
break_neck().
C++ bietet für sowas sog. Assertions.
D.h. wenn Du an einer Stelle im Programmcode eine Bedingung voraussetzt, kannst Du eine Assertion einbauen. Das Programm wird an der Stelle beendet, sollte die Bedingung doch nicht erfüllt sein. Z.B.#include <iostream> #include <string> #include <cassert> // wie oben... int main(){ // ... break_neck(red); assert( !is_alive(red) ); // Laufzeitfehler, wenn assert( false ) meet_dragon(blue); assert( !is_alive(blue) ); // dto. }Sehr hilfreich!
Und weiter noch: siehe z.B. folgenden Code, der weitestgehend identisch mit dem bisherigen ist:
#include <iostream> #include <string> #include <cassert> struct hero { std::string name; int dmg, def, hp; }; bool is_alive(hero h) { bool alive = 0<h.hp; if(alive) std::cout << h.name << " is alive.\n"; else std::cout << h.name << " is dead.\n"; return alive; } void break_neck(hero h){ // um assert() zu demonstrieren immer noch buggy :( std::cout << h.name << " slipped and broke his neck!\n"; h.hp=0; } void meet_dragon(hero& h){ std::cout << h.name << " took a wrong turn and entered the dragon's lair.\n"; h.hp=0; } // Uebergabe per Referenz auf const void print(const hero& h){ // h ist Referenz auf unveraenderliches (const) Objekt v. Typ hero std::cout << h.name << " (hp:" << h.hp << ", dmg:" << h.dmg << ", def:" << h.def << ')'; h.hp = 0; // toedliches print(). Kompiliert nicht. h ist const & ! } int main(){ hero red = { "red", 10, 10, 10 }; hero blue = { "blue", 8, 8, 8 }; print(red); std::cout << '\n'; print(blue); std::cout << '\n'; break_neck(red); assert( !is_alive(red) ); // Laufzeitfehler, wenn assert( false ) meet_dragon(blue); assert( !is_alive(blue) ); // dto. }Hoffentlich kriege ich das mit den Zeilennummern diesmal hin!

Spannend ist die Zeile 31.
h ist diesmal eine Referenz auf ein konstantes Objekt. D.h. h ist eine Referenz, wie Du sie schon kennst, aber darüberhinaus, kannst Du das Objekt, das h referenziert nicht über h verändern - siehe Zeile 33.Betrachte mit Deinem neuen Wissen Über Referenzen folgenden Code, der nicht ganz wie geplant funktioniert:
// attacking greift attacked an void attack(hero attacking, hero attacked); // Schlagabtausch - a hat die Initiative void showdown(hero a, hero b);Wie könnten Die Funktionen richtig aussehen? Kannst Du sie implementieren?
Wenn Du noch dabei bist - und folgen konntest - würde ich wohl beim nächsten Mal ein paar Worte zu Klassen und deren Member verlieren.
-
Furble Wurble schrieb:
Hoffentlich kriege ich das mit den Zeilennummern diesmal hin!

Spannend ist die Zeile 31.
Boah! Ich bin so ein Depp!

Aber jetzt ist's nur noch ein "Off-by-one"-Fehler.
Natürlich ist nur Zeile 30 spannend!
-
Hey,
Soweit ich es verstanden habe benutzt du die const variable als schutz damit sie nicht ausversehen geändert wird?
Zu den zwei funktionen. Bei der zweiten ist mir denke ich klar was zu tun ist aber bei der ersten weiss ich nicht so genau. Willst du einfach ur ne print funktionen die sagt held a mit den werten x,y,z greift held b mmit werten x,y,z an? und dann halt in er der nächsten wirklich "kämpfen" lasssen?
Ich werde mich nach der schule mal ransetzen und die dinger in deinen code einbauen.
Danke für die lehrstunde und beste Grüße aus Berlin
Photon
-
Photon schrieb:
Hey,
Moin!

Photon schrieb:
Soweit ich es verstanden habe benutzt du die const variable als schutz damit sie nicht ausversehen geändert wird?
So kannst Du es sehen.
Ein "tödliches print()" ist ein Bug, den ich so schon beim Kompilieren finde.Man spricht von "const-correctness" und die finde ich so wichtig, dass ich sie Dir von Beginn an nahelege.
Photon schrieb:
Zu den zwei funktionen. Bei der zweiten ist mir denke ich klar was zu tun ist aber bei der ersten weiss ich nicht so genau. Willst du einfach ur ne print funktionen die sagt held a mit den werten x,y,z greift held b mmit werten x,y,z an? und dann halt in er der nächsten wirklich "kämpfen" lasssen?
In der ersten sollst Du einen Angriff
attacker->attackedschreiben. Den richtigen Angriff. D.h. den Schaden berechnen und evtl.attackedLebenspunkte abziehen.
Aber es ist gar nicht der Inhalt auf den ich hinauswill: ich will nur sehen, dass Du die Funktionssignatur änderst - speziell die Typen der formalen Parameter.Dto. in der zweiten - wobei Du bei der Implementierung hoffentlich auf die erste Funktion zurückgreifst - dann wird's ein Zwei- oder Dreizeiler.
-
Ich habe jetzt eine weile nachgedacht aber ich komme nicht drauf.
Für die erste funktion habe ich das hier verwendet:
void attack(hero attacking, hero& attacked){ attacking.dmg -= attacked.def; if(attacking.dmg>0){ attacked.hp -= attacking.dmg;} }Aber wie du das jetzt mit der zweiten meinst verstehe ich nicht. Was genau soll jetzt noch bei showdown passieren? Ich steh irgendwie auf dem schlauch

-
Photon schrieb:
void attack(hero attacking, hero& attacked){ attacking.dmg -= attacked.def; if(attacking.dmg>0){ attacked.hp -= attacking.dmg;} }Aber wie du das jetzt mit der zweiten meinst verstehe ich nicht. Was genau soll jetzt noch bei showdown passieren? Ich steh irgendwie auf dem schlauch

Hmm...ja, mein Fehler. Ich dachte in
showdown()würde a erst b schlagen und b dann a - aber meinattack()ist Deinshowdown().
Egal:
Wieso übergibst Duattackingper Value?
Wieso ziehst Du vonattacking.dmgab? Verliert der Angreifer (bzw. dessen Kopie) Schadenspunkte?Meine Implementierungen:
void attack(const hero& attacker, hero& attacked){ assert(alive(attacker) && alive(attacked)); int dmg = attacked.def < attacker.dmg ? attacker.dmg - attacked.def : 0; attacked.hp = dmg < attacked.hp ? attacked.hp - dmg : 0; } void showdown(hero& a, hero& b){ attack(a, b); if(alive(b)) attack(b, a); }Der Ternäre Operator ist wahrscheinlich noch neu für Dich.
Funktioniert ganz einfach:
Bedingung ? Wert_wenn_Bedingung_wahr : Wert_wenn_Bedingung_falsch;Ist klar warum ich die Parameter so deklariere, wie ich sie deklariere?
-
achso ok ähm ja nein also:
void attack(hero attacking, hero& attacked){ attacking.dmg -= attacked.def; //<- dmg - def! if(attacking.dmg>0){ attacked.hp -= attacking.dmg;} //attacked als referenz weil von ihm hp dauerhaft abgezogen werden sollen }Also ich erzeuge für den Angreifer eine kopie damit der schaden nicht dauerhaft beeinflusst wird.
Ich habe mich die ganze zeit gefragt ob es gut ist eine funktion in einen funktion zu packen und war auch schon kurz davor das zu machen aber dann dachte ich mir das es vieleicht zu verschachtelt wird und man wieder leicht den überblick verlieren könnte.
WAs && || usw. machen weiss ich

Allerdings mag ich diese Assertmeldung nicht. Bzw. nich in diesem beispiel weil das ein hero in der rauen welt da draussen stribt ist eine zulässige aktion. Zumindest wenn es nach dem Universum geht.
-
Sry für den doppelpost hier is meine version
#include <iostream> #include <string> #include <cassert> struct hero { std::string name; int dmg, def, hp; }; bool is_alive(hero h) { bool alive = 0<h.hp; /** if(alive) std::cout << h.name << " is alive.\n"; else std::cout << h.name << " is dead.\n"; **/ return alive; } void print(const hero& h){ std::cout << h.name << " (hp:" << h.hp << ", dmg:" << h.dmg << ", def:" << h.def << ')'; } void attack(hero attacking, hero& attacked){ attacking.dmg -= attacked.def; if(attacking.dmg>0){ attacked.hp -= attacking.dmg;} } void showdown(hero& a, hero& b){ while (is_alive(a) && is_alive(b)){ attack(a,b); if (is_alive(b)){ attack(b,a);} print(a); std::cout << "\n"; print(b); std::cout << "\n"; } } int main(){ hero red = { "red", 10, 10, 10 }; hero blue = { "blue", 8, 8, 8 }; showdown(red,blue); }
-
Photon schrieb:
Also ich erzeuge für den Angreifer eine kopie damit der schaden nicht dauerhaft beeinflusst wird.
Guter, richtiger Gedanke. Allein das gesamte struct hero zu kopieren, ist aufwändiger, als über eine Referenz auf const nur lesend zuzugreifen. Siehe mein Beispiel.
Photon schrieb:
Ich habe mich die ganze zeit gefragt ob es gut ist eine funktion in einen funktion zu packen
Das ist absolut üblich. Komplexe Funktionen lassen sich so bequem aus einfachen anderen Funktionen zusammenbauen. Siehe z.B. mein showdown(): 3 Zeilen für einen Schlagabtausch von zwei Helden.
Photon schrieb:
Allerdings mag ich diese Assertmeldung nicht. Bzw. nich in diesem beispiel weil das ein hero in der rauen welt da draussen stribt ist eine zulässige aktion. Zumindest wenn es nach dem Universum geht.
Das assert() steht als erste Zeile in der Funktion. Ich will also nur verhindern, dass auf Tote eingeschlagen wird, bzw. das Tote noch angreifen.
Aha: ich sehe Du hast jetzt einen Kampf auf Leben und Tod daraus gemacht.Ich geh jetzt nochmal einen Schritt weiter.
Bisher istheroein POD - plain old data - sowas benutzt man schon seit 40 Jahren in C.C++ lässt mich zusätzlich zu Daten (name, hp, def, dmg) noch Methoden in ein struct einfügen.
Betrachte z.B. folgenden Code
#include <iostream> #include <string> struct hero { std::string name_; int hp_, dmg_, def_; // default constructor (ohne Parameter) hero() : name_("Unsung hero"), hp_(180), dmg_(80), def_(70) { /* body */ } // Ein Konstruktor mit Parametern hero(std::string name, int hp, int dmg, int def) : name_(name), hp_(hp), dmg_(dmg), def_(def) {} }; void print(const hero& h, std::ostream& out) { out << h.name_ << " (hp:" << h.hp_ << ", dmg:" << h.dmg_ << ", def:" << h.def_ << ')' << '\n'; } int main(){ using namespace std; hero h{"Parzival", 200, 100, 100}; print( h, cout ); hero hh; // default konstruierter Held print( hh, cout ); }Das sieht erstmal wild aus.
Ist allerdings im großen und ganzen unserstruct hero, wie vorher auch (ich habe nur die Daten member umbenannt z.B.name->name_)Diesmal hat hero allerdings 2 Konstruktoren. Konstruktoren sorgen dafür, dass Objekte der Klasse zu der sie gehören von Anfang an in einem definierten Zustand sind. Sieh Dir die Konstruktoren an (Zeilen 8-10 u. 12-14). Ist leicht einzusehen, was da passiert, oder?
In Zeile 24 benutze ich den Konstruktor, der Parameter entgegennimmt. In Zeile 27 benutze ich den ohne Parameter.
Beachte auch, dass jeder Konstruktor noch einen body hat - da könnte noch Code zwischen den geschweiften Klammern stehen - wie in einer Funktion.Fragen bis jetzt?
Schreib Dir vielleicht eine andere Klasse mit ein paar Membern. Schau, ob Du den Konstruktor implementiert bekommst.Das nächste Ziel heisst wohl Methoden - damit wird's richtig spannend.

-
Ok das mit dem kontruktor habe ich denke ich soweit verstanden. aber die printfunktion macht mir zu schaffen ich weiß nicht so genau was ostream da macht und meine nachforchung hat mit auch nicht schlauer gemacht.
So oder so werde ich morgen mal was kleines schreiben um das bisher gelernte anzuweden.

-
Photon schrieb:
aber die printfunktion macht mir zu schaffen ich weiß nicht so genau was ostream da macht und meine nachforchung hat mit auch nicht schlauer gemacht.
Das war eine kleine Gemeinheit, die ich eingebaut habe.
Schauen wir uns die mal an:void print(const hero&, std::ostream&);print()ist also eine Funktion, die nichts zurückgibt und zwei Parameter erwartet.
Der erste Parameter ist eine Referenz aufconst hero.
Der zweite Parameter ist eine Referenz aufstd::ostream.Dieser zweite Parameter gibt das "Ziel" für
print()vor.
std::ostreamist ein Typ aus der Standardbibliothek von C++. Dieser Typ stellt verschiedene Funktionen bereit um in einen Stream zu schreiben. Ein Stream bezeichnet in diesem Zusammenhang einen Strom von Zeichen.
std::coutist vom Typstd::ostreamund wird im Header<iostream>deklariert.
D.h.std::coutist ein vortreffliches Argument für den zweiten Parameter vonprint().
Aber auch jeder andere ostream geht, z.B. Schreiben in eine Datei:#include <iostream> #include <fstream> #include <string> struct hero { std::string name_; int hp_, dmg_, def_; hero() : name_("Unsung hero"), hp_(180), dmg_(80), def_(70) {} hero(std::string name, int hp, int dmg, int def) : name_(name), hp_(hp), dmg_(dmg), def_(def) {} }; void print(const hero& h, std::ostream& out) { out << h.name_ << " (hp:" << h.hp_ << ", dmg:" << h.dmg_ << ", def:" << h.def_ << ')' << '\n'; } int main(){ using namespace std; hero heroes[] = { {"Parzival", 200, 100, 100}, {"Bors", 205, 90, 100}, {"Lancelot", 190, 90, 90}, {}, }; ofstream file{"heroes.txt"}; // ein ofstream ist ein spezieller ostream if( !file ) return -1; // Datei konnte nicht geoeffnet werden for(const hero& h : heroes) print( h, file ); }Hausaufgabe: Schau Dir den Header
<iostream>Deiner Standardbibliothek an.
-
Hey,
ich sitze gerade an dem projekt und habe nun 2 kleine probleme
#include <iostream> struct pos{int x, y;}; struct creep {int hp; char pic;}; creep() : hp(30), pic('o') {} // Warum geht das nicht? ich bekomme fehler //error: expected unqualified-id before ')' token void print (const creep& cr){ std::cout << cr.hp << cr.pic << std::endl; } int main() { creep paul; print(paul); return 0; }Wenn ich jetzt noch pos in die sctruct creep argumente nehme:
#include <iostream> struct pos{int x, y;}; struct creep {pos p; int hp; char pic;}; creep() : pos(5,5) hp(30), pic('o') {} // Wie initialisiert man pos ? void print (const creep& cr){ std::cout << cr.p << cr.hp << cr.pic << std::endl; } int main() { creep paul; print(paul); return 0; }Desweiteren sieht der konstruktor mit werten für mich aus als ob man einfach das struct initialisiert bis auf den part in den geschweiften klammern also
creep paul={5,6,7};So wäre es ja ganz normal bei deinen konstruktor sieht es wie gesagt eigentlich genau so aus in der main ich verstehe nicht so wirklich denn sinn.
Grüße
Photon
-
Photon schrieb:
Hey,
ich sitze gerade an dem projekt und habe nun 2 kleine probleme
#include <iostream> struct pos{int x, y;}; struct creep {int hp; char pic;}; creep() : hp(30), pic('o') {} // Warum geht das nicht? ich bekomme fehler //error: expected unqualified-id before ')' token void print (const creep& cr){ std::cout << cr.hp << cr.pic << std::endl; }Der Konstruktor muss in die struct, vor dem };.
Wenn ich jetzt noch pos in die sctruct creep argumente nehme:
#include <iostream> struct pos{int x, y;}; struct creep {pos p; int hp; char pic;}; creep() : pos(5,5) hp(30), pic('o') {} // Wie initialisiert man pos ? void print (const creep& cr){ std::cout << cr.p << cr.hp << cr.pic << std::endl; } }Du kannst pos so initialisieren, wenn pos einen Konstruktor hat, der int, int nimmt.
Desweiteren sieht der konstruktor mit werten für mich aus als ob man einfach das struct initialisiert bis auf den part in den geschweiften klammern also
creep paul={5,6,7};So wäre es ja ganz normal bei deinen konstruktor sieht es wie gesagt eigentlich genau so aus in der main ich verstehe nicht so wirklich denn sinn.
Ja, so kann man auch initialisieren.
Aber wenn einige Elemente private sind, das heißt, man auf sie nicht zugreifen kann, geht das nicht.
Überhaupt ist man mit Konstruktoren viel flexibler (und btw: Seit C++11 ist das lediglich eine andere Syntax zum Konstruktoraufruf).
Aber ich rate dir wirklich, ein GUTES Lehrbuch zu C++(11) zu kaufen.
-
Jop danke nathan für die schnelle antwort
ich werde das mal mit den konstruktor bei pos. bekomme ich bestimmt hin.Ja ich war heut im laden und hab mir Primer c++ bestellt und es kommt morgen schon
leider die englische ausgabe aber das geht denke ich klar
-
Photon schrieb:
So wäre es ja ganz normal bei deinen konstruktor sieht es wie gesagt eigentlich genau so aus in der main ich verstehe nicht so wirklich denn sinn.
Ja. Anstatt einfacher Memberfunktionen ausgerechnet Konstruktoren für den Anfang zu wählen war wirklich nicht schlau von mir...
