Suche vorschäge für Projekte



  • Hallo,
    ich beschäftige mich seit ein paar Tagen mit C++ (habe leichte Kenntnisse schon in php & vor 3 Jahren habe ich ein C++ kurs belegt für Kids/Jugendliche bin jetzt fast 17).
    Ich suche ein Projekt wo ich viele Grundlagen lernen kann. Ich habe mich gerade mit einem vigenere verschlüsslsungs Programm versucht, hat auch meiner Meinung ganz gut geklappt.
    Würde mich über Projekt vorschlage freuen.



  • Das soll jetzt nicht blöd klingen, aber grundsätzlich muss man sagen, dass jemand nichts gutes gelernt hat, wenn er ohne gutes Buch gelernt hat. Wenn du sagst, du hast schon einmal C++ in einem Kurs belegt, bedeutet das für mich erstmal nichts gutes. Du könntest jetzt z.B. ein paar Codeschnippsel, von denen du denkst, sie sind dir gut gelungen, hier posten (keine 100 Zeilen bitte nur paar kleine Dinge), und dir dann das Feedback anhören. Je nachdem sind für dich dann Übungen aus einem Grundlagenbuch geeignet oder schon was höheres... wird man sehen 😉



  • Mach etwas, bei dem du modernes C++ anwenden kannst. Viele Bücher und Kurse vermitteln Steinzeitcode, der allerdings schon damals besser hätte geschrieben werden können. Von Online-Tutorials will ich gar nicht erst anfangen, erst recht nicht von Video-Tutorials...

    Beispiel: Mach ein Adressbuch mit folgenden Features, geordnet nach Schwierigkeit:

    • Eintrag speichern
    • Alle Einträge auflisten
    • Eintrag suchen und löschen
    • Speichern in Datei
    • Nach verschiedenen Kriterien wie Name, Wohnort, etc. sortieren
    • Lesen von Datei
    • Fehler bei der Eingabe und Datei erkennen
    • Nach bestimmten Masken filtern (z.B. alle Wohnorte mit bestimmten Postleitzahl-Präfix)
    • ...

    Das kannst du alles schön über die Konsole regeln. Damit du tatsächlich modernes C++ benutzt, ein paar radikale Randbedingungen:

    • Keine globalen und statischen Variablen
    • Keine manuelle Speicherverwaltung (d.h. kein new und delete )
    • Nutze die STL -- sie wird dein wichtigstes Werkzeug
    • Nutze C++-IOStreams für Datei- und Konsolenein-/ausgabe
    • Vermeide Folgendes: Rohe Arrays, alle Casts ausser static_cast , goto , Exception-Spezifikationen

    Wenn du es schaffst, nur schon einen Grossteil der Punkte zu implementieren und dich an die Einschränkungen hältst, wirst du schon einen guten Überblick über die Standardbibliothek und gängige Algorithmen und Datenstrukturen haben. Damit du möglichst viel dabei lernst, könntest du dir am Ende des Projekts überlegen, was du nun besser machen würdest. Um mit Speicherverwaltung und Zeigerarithmetik vertrauter zu werden, könntest du einzelne Datenstrukturen selbst implementieren ( std::vector und std::list sind in ihrer Grundform nicht schwierig) und in deinem Projekt zum Test einbauen.

    Ausserdem könntest du das Ganze unter Versionskontrolle (z.B. Git) setzen, wenn du das auch gleich nebenbei lernen willst 😉



  • Hier mall ein Codeschnippsel:

    void codiren(string s_clear,string s_pass){
        s_pass=Pass(s_pass,s_clear.length());
        string codirt="codirt:";
        for(int i=0;i<(int)s_clear.length();i++){
            int i_clear;
            try{
                i_clear=getATOZtoNumer(s_clear[i]);
            }
            catch(fail_data_error error){
                std::cout<<std::endl<<"Error: char'"<<error.c_error<<"'not
    acapt. "<<std::endl;
                codirt+="...";
                break;
            }
            int i_pass;
            try{
                i_pass=getATOZtoNumer(s_pass[i]);
            }
            catch(fail_data_error error){
                std::cout<<std::endl<<"Error: char'"<<error.c_error<<"'not
    acapt. "<<std::endl;
                codirt+="...";
                break;
            }
            if(i_clear+i_pass-25>0)
                codirt+=getNumbertoAtoZ(i_clear+i_pass-26);
            else
                codirt+=getNumbertoAtoZ(i_clear+i_pass);
        }
    std::cout<<codirt<<std::endl;
    }
    


  • Dinge, die direkt auffallen:

    • Überall Pass-by-Value (auch Exceptions)
    • C-Casts
    • Falsche Typen ( int statt std::size_t ) und dadurch unnötige Casts
    • Überall mit Whitespaces gespart, dadurch nicht sehr leserlich (für mich zumindest)
    • Schlechte Einrückung ( try-catch )
    • i++ statt ++i, spielt später eine Rolle
    • Iteration mit Indizes statt Iteratoren, schränkt Code unnötig ein und ist je nach Datenstruktur langsamer
    • Etwas viel lokale Fehlerbehandlung mit Exceptions, sieht fast aus wie Java. Eventuell könnte man sich lokal Error-Codes überlegen oder die Exception propagieren...
    • Mix zwischen englischen und deutschen Bezeichnern
    • Inkonsistente Namenskonvention (Gross-/Kleinschreibung innerhalb Funktionsnamen)

    Siehe auch meinen letzten Post.


  • Mod

    codieren!
    http://www.duden.de/rechtschreibung/_ieren

    Bin ja eigentlich gegen Rechtschreibflames, aber wenn es jemand so oft falsch macht, dann tut's nach einer Weile einfach nur vom Zugucken weh.

    P.S.: Was Nexus zu dem Code sagt stimmt zwar, aber nach der Beschreibung deines Lernprozesses hat hier wohl jeder weitaus schlimmeres erwartet (die meisten Leute überschätzen sich nach C++-Kursen, da sie keine guten Vergleichsmaßstäbe haben). Das sind größtenteils nur kleinere Stilfragen, die man sich schnell angewöhnen kann.



  • @Nexus
    Habe da ein part fragen.
    Wo ist der unterschied zwischen i++ ++i?
    Was meinst du das mit den unnötige casts?
    Und könntest du mir ermorden wie du das mit den Fehlerbeheben meinst?

    Sorry muss noch viel lernen 😃



  • MaCo schrieb:

    Wo ist der unterschied zwischen i++ ++i?

    ++i: präfix increment = inkrementieren bevor der Ausdruck ausgewertet wird
    i++: postfix increment = inkrementieren nachdem der Ausdruck ausgewertet wurde

    i = 2;
    ++i*2; -> 6

    i = 2;
    2*i++; -> 4

    MaCo schrieb:

    Was meinst du das mit den unnötige casts?

    for(std::string::size_type i = 0;i < s_clear.length();i++);
    // beziehungsweise:
    for(std::size_t i = 0;i < s_clear.length();i++);
    

    Aber: Hier verweise ich nochmal darauf, wie er sagte, dass iteratoren besser sind.

    for (auto const& i : s_clear /* name: meh */) {
        // i ist vom typ "const char&"
    }
    // oder:
    for (auto i = std::begin(s_clear); i != std::end(s_clear); ++i) {
        // i ist vom type std::string::iterator
    }
    

    Etwas viel lokale Fehlerbehandlung mit Exceptions, sieht fast aus wie Java. Eventuell könnte man sich lokal Error-Codes überlegen oder die Exception propagieren...

    Er meint, dass so viel exception handling hier absoluter overkill ist und nicht der geschwindigkeit oder leslichkeit des codes zuträglich ist. Alternative Ansätze sind hier vorzuziehen (wie er sagte: fehlercodes zB)


  • Mod

    MaCo schrieb:

    Wo ist der unterschied zwischen i++ ++i?

    Die beiden Ausdrücke haben einen Rückgabewert. i++ gibt den ursprünglichen Wert von i wieder und ++i gibt den bereits erhöhten Wert von i wieder. Wenn du dir mal überlegst, wie das wohl umgesetzt wird, dann kommst du zu dem Schluss, dass dahinter wohl etwas in folgender Art stecken muss:

    int praefix_plusplus(int &i)
    {
      i += 1;
      return i;
    }
    
    int suffix_plusplus(int &i)
    {
      int old_value = i;
      i += 1;
      return old_value;
    }
    

    Wie man sieht, ist der zweite Code aufwändiger, da er eine Kopie erzeugt. Für einfache Datentypen ist der Unterschied in der Regel unerheblich, da das ganz leicht wegoptimiert werden kann, wenn der Rückgabetyp nicht benötigt wird (und selbst wenn er benötigt wird, kennt der Compiler vielleicht noch ein paar Tricks, um das zu optimieren). Aber wenn i kein int mehr ist, sondern irgendein kompliziertes Objekt (Beispiel: Checked Iterator in einer Baumstruktur), dann ist möglicherweise das Kopieren nicht ganz so einfach und der Compiler kann auch nicht wirklich tricksen, da er nicht mehr vorhersehen kann, wie sich i und i + 1 zueinander verhalten. Das macht Optimierung schwierig bis unmöglich. Wenn man dann i++ schreibt, obwohl man bloß ++i braucht, hat man unnötig das Programm langsamer gemacht.

    Was meinst du das mit den unnötige casts?

    Die Casts wie

    for(int i=0;i<(int)s_clear.length();i++){
    

    hast du doch nur gemacht, weil der Compiler sich darüber beschwert hat, dass du signed und unsigned Typen vergleichst, oder? Aber:
    1. Der Cast behebt das Problem gar nicht. Wenn du vor dem Vergleich einen unsigned Typen außerhalb des Wertebereichs des signed Typen in den signed Typen castest, dann passiert genau der gleiche Müll, als wenn du sie direkt verglichen hättest, ohne den Cast. Der Cast hat bloß den Compiler dazu gebracht, das Maul zu halten, weil du ihm gesagt hast, dass du weißt, was du an der Stelle tust. Tust du in Wirklichkeit aber gar nicht.
    2. Die eigentliche Ursache ist doch, dass i ein int ist, length() aber ein unsigned Typ (genauer: ein size_t). Mach doch i auch zu einem size_t!

    Und könntest du mir ermorden wie du das mit den Fehlerbeheben meinst?

    Das ist mir zu gewalttätig.



  • SeppJ schrieb:

    Wenn du vor dem Vergleich einen unsigned Typen außerhalb des Wertebereichs des signed Typen in den signed Typen castest, dann passiert genau der gleiche Müll, als wenn du sie direkt verglichen hättest, ohne den Cast. Der Cast hat bloß den Compiler dazu gebracht, das Maul zu halten, weil du ihm gesagt hast, dass du weißt, was du an der Stelle tust. Tust du in Wirklichkeit aber gar nicht.

    Nein. -1 < 2u ist false , weil -1 in einen unsigned gecastet wird und dann (im 2er-Komplement) den grösste Wert annimmt, den unsigned hat. Wenn man hingegen die rechte Seite in einen signed int castet, hat man das Problem nicht. Das einzige Problem, das man dann hat ist, wenn length()>MAX_INT. Und damit würde ich eher nicht rechnen. Deshalb ist der Cast nach int eine in der Praxis einwandfreie Lösung. size_t zu verwenden ist fehleranfällig (underflow ist mir schon öfters passiert) und die Probleme sind rein akademischer Natur.


  • Mod

    signature schrieb:

    SeppJ schrieb:

    Wenn du vor dem Vergleich einen unsigned Typen außerhalb des Wertebereichs des signed Typen in den signed Typen castest, dann passiert genau der gleiche Müll, als wenn du sie direkt verglichen hättest, ohne den Cast. Der Cast hat bloß den Compiler dazu gebracht, das Maul zu halten, weil du ihm gesagt hast, dass du weißt, was du an der Stelle tust. Tust du in Wirklichkeit aber gar nicht.

    Nein. -1 < 2u ist false , weil -1 in einen unsigned gecastet wird und dann (im 2er-Komplement) den grösste Wert annimmt, den unsigned hat. Wenn man hingegen die rechte Seite in einen signed int castet, hat man das Problem nicht. Das einzige Problem, das man dann hat ist, wenn length()>MAX_INT. Und damit würde ich eher nicht rechnen. Deshalb ist der Cast nach int eine in der Praxis einwandfreie Lösung. size_t zu verwenden ist fehleranfällig (underflow ist mir schon öfters passiert) und die Probleme sind rein akademischer Natur.

    😕 Das hab ich doch gesagt.

    P.S.: 💡 Oh, ich hab's. Ich hab "genau" geschrieben, obwohl es doch noch einen nicht erwähnten Unterschied gab. Schande über mich, dass ich wieder nicht gemerkt habe, dass ich hier doch in der Matheklausur bin und nicht in einem Forum in dem man mit normalen Menschen mit Umgangssprache kommuniziert.



  • Trotzdem: casts sind nicht (immer) kostenlos.



  • Tim06TR schrieb:

    Trotzdem: casts sind nicht (immer) kostenlos.

    unsigned int -> int aber schon.



  • Ich habe mal versucht so viele Anregungen die verstanden haben um zu setzten. Ist das so besser?

    void codiren(string s_clear,string s_pass){
    	s_pass=Pass(s_pass,s_clear.length());
    	string codirt="codirt:";
    	for(size_t i=0;i<s_clear.length();++i){
    		int i_clear,i_pass;
    		try{
    			i_clear=getatoztonumer(s_clear[i]);
    			i_pass=getatoztonumer(s_pass[i]);
    		}
    		catch(fail_data_error error){
    			std::cout<<std::endl<<"Error: char'"<<error.c_error<<"'not acapt. "<<std::endl;
    			codirt+="...";
    			break;
    		}
    		if(i_clear+i_pass-25>0)
    			codirt+=getnumbertoatoz(i_clear+i_pass-26);
    		else
    			codirt+=getnumbertoatoz(i_clear+i_pass);
    	}
    	std::cout<<codirt<<std::endl;
    }
    

    Was würdet ihr mir vorschlagen um mir gute Grundlagen zu lernen? Bis her habe ich immer nur versucht das meine Programme irgendwie lauft, ich würde aber gerne mehr verstehen und effizienter Code schreiben. Was für Literatur würdet ihr mir empfehlen?



  • Hier findest du eine gute Liste von Büchern, die auch ständig aktuell gehalten wird.



  • gibt es auch gute Bücher in deutsch? Ich weise zwar das ich später um englisch nicht vorbeikomme, würde aber anzufangen aus Verständnis Schwierigkeiten lieber mit deutscher Lektüre anfangen.



  • MaCo schrieb:

    Ist das so besser?

    Den zuerst genannten (pass by value) und aus meiner Sicht wichtigsten Punkt hast du noch nicht beachtet.
    Und nochmal: dieses 'codirt' versteht man nicht, da man (oder zumindest ich) da was englisches rein interpretiere (co-dirt, 'gemeinsamer Dreck'?).



  • Okey das mit den Namen muss ich verandern.
    Ich versteh nur leider nicht was ihr mir (pass by value) meint; Was muss wann anders/warum?



  • Übergib Objekte als Wert (by Value), wenn du eine Kopie oder ein Move (Besitz-Transfer) möchtest. Oder falls es sich um einfache Typen handelt.

    Ansonsten, übergib Objekte als Referenzen (oder Zeiger).



  • Ist das so richtig angewandt?

    i_clear=getatoztonumer(&s_clear[i]);
    
    int getatoztonumer (char* c_char){
    	//a[97]-z[122]|A[65]-Z[90]
    	if((*c_char>=97&&*c_char<=122)||(*c_char>=65&&*c_char<=90)){
    		if(*c_char>=97&&*c_char<=122){
    			return *c_char-97;
    		}else{
    			return *c_char-65;
    		}
    	}else{
    		throw fail_data_error(*c_char);
    		return 0;
    	}
    }
    

Log in to reply