Erstes kleines Konsolenprogramm zum Bewerten



  • Hallo Leute,

    ich arbeite nun seit einigen Tagen mit C++ und wollte euch mal hier meinen Code vorstellen und ihn mal zur Korrektur bzw. Feedback vorlegen.

    Es ist ein simples kleines Programm, das auf der Konsole startet.
    Was macht es?
    Es ist ein Gästebuch, d.h. man trägt seinen Namen, EIntrag ein und das Ergebnis wird in einer Textdatei gespeichert zusätzlich mit einem Timestamp.
    Die Eingaben des Users werden auch überprüft. Z.B. muss man beim Namen mindestens 2 Buchstaben, beim EIntrag mindestens 5 eingeben.

    Ich habe mir auch noch eine zusätzliche Hilfsklasse mit einer statischen Methode erstellt, die mir int in String umwandelt und den wert returnt.

    Ich hoffe, jemand kann sich mal die Zeit nehmen, ein Blick drüberwerfen und mir Verbesserungsvorschläge, oder elegantere Lösungen zu meinem Projekt vorzeigen.

    Danke im voraus und hier der Code:

    **
    castutils.h**

    #ifndef CASTUTILS_H
    #define CASTUTILS_H
    
    #include <string>
    
    using namespace std;
    
    class CastUtils{
        public:
           static string intToString(int i);
    };
    
    #endif // CASTUTILS_H
    

    gaestebuch.h

    #ifndef GAESTEBUCH_H
    #define GAESTEBUCH_H
    
    #include <iostream>
    #include <string>
    using namespace std;
    
    class Gaestebuch{
        public:
            Gaestebuch();
            ~Gaestebuch();
    
        private:
            bool checkInputs(string name, string entry);
    
    };
    
    #endif // GAESTEBUCH_H
    

    castutils.cpp

    #include "castutils.h"
    #include <sstream>
    #include <string>
    
    using namespace std;
    
    string CastUtils::intToString(int i){
        ostringstream str;
        str << i;
        return str.str();
    }
    

    gaestebuch.cpp

    #include "gaestebuch.h"
    #include "castutils.h"
    #include <string>
    #include <sstream>
    #include <iostream>
    #include <fstream>
    #include <time.h>
    
    using namespace std;
    
    Gaestebuch::Gaestebuch(){
        string name;
        string entry;
        bool end = false;
        string decision;
    
        ofstream txtFile;
        txtFile.open("Gaestebuch.txt", ios::app);
    
        while(!end){
            cout << "***************************************" << endl;
            cout << "Name:" << endl;
            cin.sync(); //nötig wegen getline()
            getline(cin, name);
            cout << endl;
            cout << "Eintrag:" << endl;
            getline(cin, entry);
            cout << endl;
    
            if(checkInputs(name, entry)){
                time_t timestamp = time(0);
                tm *now = localtime(&timestamp);
                string time =
                        " am "+CastUtils::intToString(now->tm_mday) +
                        "."+CastUtils::intToString(now->tm_mon)+
                        "."+CastUtils::intToString(now->tm_year+1900)+
    
                        " um "+CastUtils::intToString(now->tm_hour)+
                        ":"+CastUtils::intToString(now->tm_min)+
                        " Uhr";
    
                txtFile << "von " << name << time << endl
                        << entry << endl
                        << endl;
            }
            else{
                cout << "#Fehler! Bitte pruefen Sie Laenge Ihrer Eingaben(Name min 2 Zeichen, Eintrag min 5 Zeichen.)" << endl << endl;
            }
    
            cout << "Weiteren Eintrag hinzufuegen?(J = Ja - N = Nein)" << endl;
            getline(cin, decision);
            if(decision == "N"){
                end = true;
                txtFile.close();
                cout << endl;
            }
        }
    }
    
    bool Gaestebuch::checkInputs(string name, string entry){
        if(name.length() <= 2 && entry.length() <= 5){
            return false;
        }
        return true;
    }
    

    main.cpp

    #include <gaestebuch.h>
    
    void main()
    {
        Gaestebuch *gb = new Gaestebuch;
    }
    


  • Die Klassen sind bei dir vollkommen sinnlos. Das komplette Programm kann genau so auch mit freien Funktionen implementiert werde.
    void main muss int main sein.
    Warum new in der main?



  • Das ist quasi ganz alter C++-Style, wo Klassen noch das tollste, neuste und beste waren, sodass man unbedingt alles in Klassen packen musste.
    Kapsel dir doch lieber einen Gästebucheintrag, speicher im Gästebuch einen vector<Eintrag> , mach den Timestamp mit <chrono> und lass das new/delete (delete hast du vergessen) dort, wo es hingehört - im tiefen Sumpf der für Anfänger gefährlichen Programmiersünden.
    Versuch jeder Klasse ihre Aufgaben zu geben, und die, welche nicht die ihren sind, reicht sie an andere Klassen weiter.



  • und

    using namespace std;
    

    in Headerdateien - keine gute Idee.



  • Hmm ich seh schon, es gibt viel zu lernen. Zeit habe ich ja 🙂
    Ich lerne das momentan privat und investiere ca 8 Stunden / Woche in c++.

    @mani66
    Ist es schlecht new in der main zu haben?
    void geht doch auch, da meine main-Methode nichts als Rückgabewert erwartet, dachte ich.

    @ der tobi
    Ok, in die von dir genannten Stichworte muss ich mich noch einlesen.
    Warum ist denn new gefährlich?
    Ist das nicht das gleiche:

    Testobject testobj;
    

    wie

    Testobject *testobj = new Testobject
    

    Ich meine damit folgendes:
    Beides erzeugt doch ein Objekt im Speicher.
    Bei erstem greift man so auf methoden zu :

    testobj.testMethode();
    

    beim anderen so:

    testobj->testMethode()
    

    @f-th
    Aber würde die Headerdatei denn ohne using namespace std; funktionieren? Weil ich verwende ja string in der Headerdatei und braucht die nicht das namespace?



  • Kenan schrieb:

    Ist es schlecht new in der main zu haben?

    Es ist schlecht new zu verwenden ohne zu verstehen was es ist und was es tut.

    Kenan schrieb:

    void geht doch auch, da meine main-Methode nichts als Rückgabewert erwartet, dachte ich.

    Die Funktion main() gibt laut Standard einen int zurück. Gibt sie per return nichts zurück so wird implizit 0 zurückgegeben.

    Kenan schrieb:

    Warum ist denn new gefährlich?
    Ist das nicht das gleiche:

    Testobject testobj;
    

    wie

    Testobject *testobj = new Testobject
    

    Ich meine damit folgendes:
    Beides erzeugt doch ein Objekt im Speicher.

    Die Frage ist nur, in welchem Speicher. Variablendeklarationen legen die Variablen die Variable auf den Stack. So deklarierte automatische Variablen haben eine festgelegte Lebensdauer. Mit new wird Speicher auf dem Heap reserviert, der solange lebt, bis er mit delete wieder freigegeben wird.

    Kenan schrieb:

    Bei erstem greift man so auf methoden zu :

    testobj.testMethode();
    

    beim anderen so:

    testobj->testMethode()
    

    Hat mit dem Verständnis von automatischen Variablen vs. Pointer auf objekte am Heap nur am Rande zu tun.

    struct foo_t { void func() {} };
    
    int main()
    {
        foo_t foo;
        foo_t *bar = &foo;
    
        bar->func();
    }
    

    ... ich sehe da kein new .

    Kenan schrieb:

    Aber würde die Headerdatei denn ohne using namespace std; funktionieren? Weil ich verwende ja string in der Headerdatei und braucht die nicht das namespace?

    #ifndef GAESTEBUCH_H
    #define GAESTEBUCH_H
    
    // #include <iostream> für diesen Header unnötig.
    #include <string>
    
    class Gaestebuch{
        public:
            Gaestebuch();
            ~Gaestebuch();
    
        private:
            bool checkInputs(std::string name, std::string entry);
    
    };
    
    #endif // GAESTEBUCH_H
    


  • Gegenvorschlag:

    tools.h

    #ifndef TOOLS_H_INCLUDED
    #define TOOLS_H_INCLUDED TOOLS_H_INCLUDED
    
    #include <iostream>
    
    void clear( std::istream &is );
    
    #endif /* TOOLS_H_INCLUDED */
    

    tools.cpp

    #include "tools.h"
    
    #include <limits>
    
    void clear( std::istream &is )
    {
    	is.clear();
    	is.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );
    }
    

    entry.h

    #ifndef ENTRY_H_INCLUDED
    #define ENTRY_H_INCLUDED ENTRY_H_INCLUDED
    
    #include <string>
    
    class entry_t {
    
    	friend std::istream& operator>> ( std::istream &is, entry_t &entry );
    	friend std::ostream& operator<< ( std::ostream &os, entry_t const &entry );
    	friend std::ifstream& operator>> ( std::ifstream &is, entry_t &entry );
    	friend std::ofstream& operator<< ( std::ofstream &os, entry_t const &entry );
    
    	private:
    		std::string name;
    		std::string message;
    };
    
    #endif /* ENTRY_H_INCLUDED */
    

    entry.cpp

    #include "entry.h"
    
    #include <iostream>
    #include <fstream>
    
    #include "tools.h"
    
    std::istream& operator>> ( std::istream &is, entry_t &entry )
    {
    	std::cout << "Name:    ";
    	clear( is );
    	std::getline( is, entry.name );
    	std::cout << "Message: ";
    	std::getline( is, entry.message );
    	return is;
    }
    
    std::ostream& operator<< ( std::ostream &os, entry_t const &entry )
    {
    	os	<< "Name:    " << entry.name << '\n'
    		<< "Message: " << entry.message << '\n';
    	return os;
    }
    
    std::ifstream& operator>> ( std::ifstream &is, entry_t &entry )
    {
    	std::getline( is, entry.name );
    	std::getline( is, entry.message );
    	return is;
    }
    
    std::ofstream& operator<< ( std::ofstream &os, entry_t const &entry )
    {
    	os	<< entry.name << '\n'
    		<< entry.message << '\n';
    	return os;
    }
    

    guestbook.h

    #ifndef GUESTBOOK_H_INCLUDED
    #define GUESTBOOK_H_INCLUDED GUESTBOOK_H_INCLUDED
    
    #include <vector>
    #include <string>
    
    #include "entry.h"
    
    class guestbook_t
    {
    	friend std::ostream& operator<< ( std::ostream& os, guestbook_t const &gb );
    
    	private:
    		std::string const filename;
    		std::vector< entry_t > entries;
    
    	public:
    		guestbook_t( std::string const &filename );
    		void add_entry( entry_t const &entry );
    		void save();
    };
    
    #endif /* GUESTBOOK_H_INCLUDED */
    

    guestbook.cpp

    #include "guestbook.h"
    
    #include <exception>
    #include <fstream>
    
    guestbook_t::guestbook_t( std::string const &filename )
    	: filename( filename )
    {
    	std::ifstream file( filename );
    	if( !file.is_open() ) throw std::runtime_error(
    		std::string( std::string( "Error: The file \"" ) + filename + "\" could not be opened!\n" ).c_str() );
    
    	entry_t current_entry;
    
    	while( file >> current_entry ) {
    		entries.push_back( current_entry );
    	}
    }
    
    void guestbook_t::add_entry( entry_t const &entry )
    {
    	entries.push_back( entry );
    }
    
    void guestbook_t::save()
    {
    	std::ofstream file( filename, std::ios::trunc );
    
    	if( !file.is_open() ) throw std::runtime_error(
    		std::string( std::string( "Error: The file \"" ) + filename + "\" could not be opened!\n" ).c_str() );
    
    	for( auto it = entries.begin(); it != entries.end(); ++it )
    		file << *it;
    }
    
    std::ostream& operator<< ( std::ostream& os, guestbook_t const &gb )
    {
    	for( auto it = gb.entries.begin(); it != gb.entries.end(); ++it )
    		os << *it << '\n';
    	return os;
    }
    

    main.cpp

    #include "tools.h"
    #include "guestbook.h"
    
    int main()
    {
    	char const * guestbook_filename = "guestbook.txt";
    
    	try {
    		guestbook_t guestbook( guestbook_filename );
    		int unsigned choice = 0;
    
    		do {
    			std::cout << "[1] view entries\n[2] new entry\n[3] save to file\n[4] exit\n";
    
    			while( !( std::cin >> choice ) || ( choice < 1 || choice > 4 ) ) {
    
    				clear( std::cin );
    				std::cerr << "Invalid Input!\n";
    			}
    
    			switch( choice ) {
    
    				case 1:
    					std::cout << guestbook << '\n';
    					break;
    
    				case 2: {
    					entry_t entry;
    					std::cin >> entry;
    					guestbook.add_entry( entry );
    					break;
    				}
    
    				case 3:
    					guestbook.save();
    					break;
    
    				case 4:
    					break;
    			}
    
    		} while( choice != 4 );
    
    	} catch( std::exception &e ) {
    
    		std::cerr << e.what();
    		return EXIT_FAILURE;
    	}
    }
    


  • hey swordfish, das ist echt nett von dir, dass
    du dir die zeit genommen hast, den Code zu verbessern.
    Ich denke an deiner Verbesserung kann ich noch ne Menge
    lernen.
    Ich studiere mal den Code nun Zeile für Zeile durch und
    falls Fragen auftauchen rühre ich mich 🙂


Log in to reply