Vektor-Funktion bricht nach einer Eingabe ab



  • @Suprax sagte in Vektor-Funktion bricht nach einer Eingabe ab:

    aber ich möchte den so einfach halten wie möglich

    Das ist ja das Problem. std::find() ist einfach. Einfach zu erlernen, einfach zu lesen und einfach viel wenig Fehleranfällig als immer wieder Schleifen hinzuschreiben wie Du es in Deinen letzen Codeschnippeln gemacht hast. Mein Code sollte Dir eigentlich zeigen wie sehr einfach es sein kann. Du solltest wirklich, wirklich alles vergessen was Du Dir durch irgendwelche utube-Videos angeeignet hast, das Buch vom Struppi nehmen und von vorne anfangen. Ganz vorne. Wenn was unklar ist beim Durcharbeiten des Buches: fragen.



  • Moin an Alle,
    ich habe mein Programm so gut wie fertig geschrieben.
    @Swordfish Ich möchte an der Stelle anmerken, dass ich mir deinen Rat bzgl. des Buches zu Herzen nehmen werde, allerdings muss ich erstmal bis zu den Klausuren mit meinen Kenntnissen arbeiten und kann nicht sozusagen von 0 das ganze Buch von vorne anfangen, daher versuch nicht allzu sehr Augenschmerzen beim Quellcode zu bekommen 😃

    OnlineVideothek::OnlineVideothek() {}
    
    OnlineVideothek::~OnlineVideothek() {}
    
    void OnlineVideothek::createUser(string name, string password) {
        
        //Alternative
        /*Benutzer b(name, password); 
        this->users.push_back(b);*/
        
        bool notFound = true;
        for (unsigned int i=0; i < users.size(); i++) {
            
            if ( name == users.at(i).get_User() ) {
                
                cout << "Name ist bereits belegt!";
                notFound = false;
                break;
            }
        }
        if(notFound)
        {
            this->users.push_back(Benutzer(name, password));
        }
    }
    
    void OnlineVideothek::createVideo(string title, int maxViews) {
        
        bool notFound = true;
        
        for (unsigned int i=0; i < videos.size(); i++) {
            
            if (title == videos.at(i).getTitel()){
                
                cout << "Titel ist bereits vorhanden!";
                notFound = false;
                break;
            }
        }
        
        if (notFound) {
        
            this->videos.push_back(Video(title,0,maxViews));
        
        }
    }
    
    vector<Video> OnlineVideothek::getVideos() const {
        
        return this->videos;
    }
    
    vector<Benutzer> OnlineVideothek::getUsers() const {
        
        return this->users; 
    }
    
    bool OnlineVideothek::login(string name, string password) {
        
        for (unsigned int i=0; i < this->users.size(); i++) {
            
            if ( name == this->users.at(i).get_User() && password == this->users.at(i).get_Password() )
            {    
                return true;
            } 
            else 
            {
                
                return false;
            }
        }
        
        return false; 
    }
    
    void OnlineVideothek::showAllVideos() const {
        
        for (unsigned int i = 0; i < videos.size(); i++) {
            
            cout << "Video: ";
            videos.at(i).print(); 
            
        }
        
    }
    
    void OnlineVideothek::addToFavorites(string name, int id) {
        
        
        bool foundUser = false;
        bool foundVideo = false;
        int userIndex = -1;
        int videoIndex = -1;
        
        for (unsigned int i = 0; i < users.size(); i++) 
        {
            if(users.at(i).get_User() == name)
            {
                
                foundUser = true;
                userIndex = i;
                
            }
            else {
                
                throw runtime_error {"Benutzer nicht gefunden!"};
            }
        }
        
        
        for (unsigned int i = 0; i < videos.size(); i++) 
        {
            if (videos.at(i).getID() == id )
            {
                foundVideo = true;
                videoIndex = i;
            }
            else {
                
                throw runtime_error {"Video nicht gefunden!"};
            }
        }
        
        
        if (foundUser && foundVideo) 
        {
            users.at(userIndex).fillFavourite(videos.at(videoIndex).getID());
        }
    }
    
    void OnlineVideothek::watch(string name, int id) {
        
        int current_video = -1;
        int current_watching = -1; 
        bool videoFound = false;
        bool userFound = false;
        
        for ( unsigned int i = 0; i < this->videos.size(); i++ ) {
            
            if ( id == videos.at(i).getID() ) {
                
                videoFound = true; 
                current_video = i; 
                
            }  
        }
        
        for ( unsigned int i = 0; i < this->users.size(); i++) {
            
            if ( name == users.at(i).get_User()) {
                
                userFound = true; 
                current_watching = i;
            }
        }
        
        if ( userFound && videoFound )
        {
            this->users.at(current_watching).setCurrent_video(videos.at(current_video).getID());
            
            if ( videos.at(current_video).getLimit() > videos.at(current_video).getViews()) {
            this->videos.at(current_video).addViews(); }
            
            
        }  
    }
    
    Video OnlineVideothek::findVideo(int id) {
        
        for (unsigned int i = 0; i < this->videos.size(); i++) {
            
            if (this->videos.at(i).getID() == id ) {
                
                return this->videos.at(i); 
            }
            
        }
        
    }
    
    Benutzer OnlineVideothek::findUser(string name) {
        
        for (unsigned int i = 0; i < this->users.size(); i++) {
            
            if (this->users.at(i).get_User() == name) {
                
                return this->users.at(i); 
            }
        }
        
    }
    
    Benutzer::Benutzer() { }
    
    Benutzer::Benutzer(string u, string pw) : user(u), password(pw) {}
    
    Benutzer::~Benutzer() {}
    
    
    void Benutzer::setCurrent_video(int current_video) { //kann sich aendern, guckt nicht nur ein Video, sondern mehrere
        
        this->current_video = current_video; 
    }
    
    
    
    string Benutzer::get_User() const {
        
        return user; 
    }
    
    string Benutzer::get_Password() const {
        
        return password; 
    }
    
    vector<int> Benutzer::getFavourite() const {
        
        return this->myFavourite; 
    }
    
    void Benutzer::print() {
    
        
            cout << "Benutzer-ID: " << user << endl << "Passwort: " << password << endl;
    }
    
    void Benutzer::fillFavourite(int id) {
        
        this->myFavourite.push_back(id); //ich lege in meinen Vektor myFavourite jedesmal die ID 
          
        
    }
    
    
    using namespace std;
    
    
    Video::Video( string t, int v, int l) : titel(t), views(v), limit(l) {
    
        id = counter; 
        counter++; 
    }
    
    Video::~Video() {}
    
    int Video::getID() const {
    
        return id;
    }
    
    string Video::getTitel() const {
    
        return titel;
    }
    
    void Video::setViews(int views) {
        
        this->views = views;  //Wir haben einmal views außerhalb und innerhalb der Klasse, die mit dem This sich auf 
                              //das views von dem Parameter der mit setViews übergeben wird
    }
    
    void Video::addViews() {
        
        this->views++; 
        
    }
    
    int Video::getViews() const {
    
        return views;
    }
    
    int Video::getLimit() const {
    
            return limit;
    }
    
    void Video::print() const {
        
        if (limit > 8 || limit < 0) {
    
            throw runtime_error("Ungueltiger Zahlenbereich!");
        } else {
    
        cout << "ID:" << id << " Titel:" << titel << " Views:" << views << " Limit:" << limit << endl;
    
        }
        
    }
    
    #include "Video.h"
    #include "Benutzer.h"
    #include "OnlineVideothek.h"
    #include "myError.h"
    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    int main() try
    {
    
        OnlineVideothek test; 
        
        bool end = false;
        
        do{
            string current_user; 
            int eingabe {}; 
            
            cout << "1. Video hinzufuegen: " << endl;
            cout << "2. Login: " << endl;
            cout << "3. Favoriten anzeigen: " << endl;
            cout << "4. Zu Favoriten hinzufuegen: " << endl;
            cout << "5. Videos anzeigen: " << endl;
            cout << "6. Nur neue Videos anzeigen: " << endl;
            cout << "7. Film ansehen: " << endl; 
            
            cin >> eingabe; 
            
            switch (eingabe) {
                           
                case 1: {
                    string name; 
                    int maxViews; 
                    
                    cout << "Name des Videos: " << endl;
                    cin >> name;
                    cout << "Anzahl der max Views: ";
                    cin >> maxViews; 
                    test.createVideo(name, maxViews);
                    cout << "Video " << name << "wurde erfolgreich hinzugefuegt!" << endl; 
                     
                }
                
                case 2: {
                    string name;
                    string password;
                    cout << "Bitte ID eingeben: ";
                    cin >> name;
                    cout << "Nun das Passwort: ";
                    cin >> password;
                    cout << endl; 
                    
                  
                        
                        if (test.login(name,password)) {
                           
                            cout << "Login wurde erfolgreich ausgefuehrt!"; 
                            current_user = name; 
                        }
                     
                }
                
                case 3: {
                    
                    for ( unsigned int i = 0; i < test.getUsers().size(); i++) {
                        
                        if (test.getUsers().at(i).get_User() == current_user ) {
                            
                            for ( unsigned int j = 0; j < test.getUsers().at(i).getFavourite().size(); j++) {
                           
                                test.findVideo(test.getUsers().at(i).getFavourite().at(j)).print();
                            }
                            
                        }
                        
                    }
                    cout << endl;
                    
                }
                
                case 4: {
                    int id {}; 
                    
                    cout << "Gib eine ID eines Videos ein: ";
                    cin >> id;
                    test.addToFavorites(current_user,id);
                     
                }
                
                case 5: {
                    
                    cout << "Alle deine Videos: ";
                    test.showAllVideos();
                    
                    
                }
                
                case 6: {
                    
                    cout << "Alle deine neuen Videos: ";
                    bool found = false;
                    
                        for(unsigned int j = 0; j < test.getVideos().size(); j++)
                        {
                            
                            for(unsigned int i = 0; i < test.findUser(current_user).getFavourite().size(); i++)
                            {
                                if(test.getVideos().at(j).getID() == test.findUser(current_user).getFavourite().at(i))
                                {
                                    found = true;
                                }
                                else {
                                    
                                    found = false; 
                                }
                            
                            }
                            
                        }
                     
                   }
                
                case 7: {
                    
                    int id; 
                    
                    cout << "Bitte ID eingeben: ";
                    cin >> id;
                    
                    test.findVideo(id).addViews();
                    test.findUser(current_user).setCurrent_video(id); 
                     
                }
            }
            
        
        }while(!end);
        
            return 0; 
            
    }
     catch (std::exception& e) {
        std::cerr << "StdLib Ausnahme: " << e.what();
        return 1;
    } catch (...) {
        std::cerr << "Unbekannte Ausnahme\n";
        return 2;
    }
    

    Das ist mein jetziger Quellcode.
    Er läuft soweit, nur gibt es noch 2 kleine Fehler:

    1. Er führt die Switch-Case Reihe nicht richtig aus. Setze ich kein Break, läuft er von Case 1 zu Case 2, ohne dass der Benutzer eine Eingabe getätigt hast.
      Allerdings soll der Benutzer bei jedem Case neu entscheiden, ob er zu Case 1 oder Case 5 z.b. springt und die Eingabe muss dementsprechend gespeichert werden.

    2. Wenn ich Case 1 ausführe, kann ich nicht "Der Pate" eingeben, sondern nur "Pate" oder "Der", da er anscheinend die zwei Wörter für jeweils einen String hält.
      Kann ich diese Problematik umgehen, ohne noch eine string variable einzuführen?



  • @Suprax sagte in Vektor-Funktion bricht nach einer Eingabe ab:

    Das ist mein jetziger Quellcode.
    Er läuft soweit, nur gibt es noch 2 kleine Fehler:

    1. Er führt die Switch-Case Reihe nicht richtig aus. Setze ich kein Break, läuft er von Case 1 zu Case 2, ohne dass der Benutzer eine Eingabe getätigt hast.

    Standardmäßig solltest du jedes case mit einem break beenden. Hier fehlt es dir in alles cases! Es ist außerdem ungewöhnlich, hinter dem case einen Block mit { anzufangen.
    Also richtig:

    switch (irgendwas) {
      case 1:
         cout << "Fall 1";
         cout << "\n";
         break;
       case 2:
         cout << "Fall 2 -";
         cout << " hier noch ein weiterer Befehl\n";
         break;
       // und so weiter
    }
    

    Allerdings soll der Benutzer bei jedem Case neu entscheiden, ob er zu Case 1 oder Case 5 z.b. springt und die Eingabe muss dementsprechend gespeichert werden.

    ??? Verstehe ich nicht. Genau das erreichst du doch mit break.

    1. Wenn ich Case 1 ausführe, kann ich nicht "Der Pate" eingeben, sondern nur "Pate" oder "Der", da er anscheinend die zwei Wörter für jeweils einen String hält.

    Lies den String mit std::getline(std::cin, name) ein statt mit cin >> name

    Weiterer Fehler:
    du hast eine Funktion Benutzer OnlineVideothek::findUser(string name). Überlege dir mal, was passiert, wenn der Benutzer nicht gefunden wird. Wenn du diese Funktion berichtigst, kannst du sie auch sinnvoll im Code verwenden.



  • @wob habs überall angepasst. Die breaks sind drinne.

    Lies den String mit std::getline(std::cin, name) ein statt mit cin >> name
    

    Habe ich auch implementiert, allerdings nimmt er dann gar keine Eingabe für den Titel entgegen und lässt das Feld leer?

    Wegen der findUser(string name)-Problematik:
    Wenn er den User nicht findet, dann sollte er eine Fehlermeldung ausgeben und das Programm sollte dann nicht mehr laufen.

    Was ich an meinem Programm festgestellt habe:
    Ich kann beliebig viele Videos hinzufügen, das klappt. Auch, dass alle gespeichert werden unter unterschiedliche IDs funktioniert. Aber wenn ich das Video zu den Favoriten hinzufügen möchte, sprich ich erstelle vorher zwei Videos:

    1. Der Pate
    2. Matrix
      kann ich lediglich Der Pate zu den Favoriten hinzufügen.
      Der Film Matrix erscheint zwar, wenn ich alle erstellten Videos ausgebe lasse, allerdings lässt er sich nicht mit der ID zu den Favoriten hinzufügen. Die IDs, die die Filme automatisch zugewiesen bekommen sind feste Werte


  • @Suprax: Stell mal die Warnstufe deines Compiler höher, bzw. beachte die Warnungen (u.a. die find-Funktionen haben keine bzw. nicht genügend return-Anweisungen).
    Und bei der login-Funktion solltest du noch mal den else-Zweig überdenken (aber wie @wob schon schrieb, benutze hier besser die FindUser-Funktion - ebenso in vielen der anderen Funktionen).
    Und die Schleifen in der main-Funktion (bei case 3 und case 6) solltest du auch durch passende Funktionsaufrufe ersetzen.

    @wob sagte in Vektor-Funktion bricht nach einer Eingabe ab:

    Es ist außerdem ungewöhnlich, hinter dem case einen Block mit { anzufangen.

    Aber dies wird benötigt, wenn man lokale Variablen dort definiert (wie im Code von @Suprax)!



  • @Th69 sagte in Vektor-Funktion bricht nach einer Eingabe ab:

    @wob sagte in Vektor-Funktion bricht nach einer Eingabe ab:

    Es ist außerdem ungewöhnlich, hinter dem case einen Block mit { anzufangen.

    Aber dies wird benötigt, wenn man lokale Variablen dort definiert (wie im Code von @Suprax)!

    Dann ist aber der Block für den case tendenziell zu lang und sollte in eine Funktion ausgelagert werden. Und das ist hier der Fall.

    Also besser:

    switch (eingabe) {
        case 1: 
            useraction_add_video(test);
            break;
        case 2: 
            useraction_login_user(test);
            break;
        case 3:
            useraction_list_favorites(test);
            break;
        // ...  
    

    Wobei es irgendwie komisch ist, den Login (case 2) da in dieser Liste zu haben.

    PS: Habe ich schon mal irgendwann erwähnt, dass ich kein Fan von switch-case bin? Oft wird der Code damit unübersichtlich.



  • @Th69 der compiler ist auf More Warnings eingestellt, aber Warnungen bzgl. der find-Funktion tauchen nicht auf.

    Die Funktion weist doch eine return-Funktion auf:
    return this->users.at(i);

    oder müsste am Ende der find-Funktion return 0 erfolgen?

    Bzgl. des logins kann ich ja eine erneute Abfrage des Logins für den else-Zweig implementieren.

    @wob Das auslagern in eine Funktion geht ja flott, da die Funktionen schon stehen. Das mache ich dann noch, sobald ich die kleinen Probleme aus der Welt geschafft habe.
    Dass ich in Case 2 den Login habe, ist vorgegeben aus der Altklausur-Aufgabe. Ich stelle mir aber auch hier die Frage:
    Der Login ist dafür gedacht, eine Abfrage der Daten, bestehend aus Benutzername und Passwort, zu starten. Normalerweise müsste der Benutzer vorher eine Art Registrierung durchlaufen, damit die Login-Funktion einen Sinn macht, oder liege ich da falsch?



  • @Suprax sagte in Vektor-Funktion bricht nach einer Eingabe ab:

    @Th69 der compiler ist auf More Warnings eingestellt, aber Warnungen bzgl. der find-Funktion tauchen nicht auf.
    Die Funktion weist doch eine return-Funktion auf:
    return this->users.at(i);
    oder müsste am Ende der find-Funktion return 0 erfolgen?

    JEDER Weg aus der Funktion muss ein return haben!

    return 0 geht nicht, denn 0 ist ja kein Benutzer, sondern eine Zahl. Du könntest aber z.B. einen Pointer auf den Benutzer returnen und mit nullptr "nicht gefunden" markieren.

    PS: Deine Logik in case 6 ist auch falsch. Überleg mal, wann bei dir true und wann false rauskommt.



  • @wob so ungefähr?

    return this->users nullptr; 
    

    Zu Case 6: Ich iteriere durch mein Benutzer Vektor und mein Video-Vektor, wenn die ID des Videos gleich der ID des Favoritenvideos ist , dann gibt er Truezurück, andernfalls wurde eines vom beiden nicht gefunden, daher false.
    Oder wo sitzt mein Denkfehler?



  • Einen nullptr kannst du nicht zurückgeben, da die Funktion ja ein Benutzer-Objekt zurückgeben soll (keinen Zeiger).
    Du könntest aber am Ende wenigstens eine Exception werfen (wie in addToFavorites, welche du dann auch entsprechend umschreiben kannst, so daß sie diese find-Funktionen aufruft).

    Noch besser wäre es dann jedoch, die Funktion würde statt einer Kopie eine (konstante) Referenz zurückgeben, also

    const Benutzer& OnlineVideothek::findUser(string name)
    // bzw.
    Benutzer& OnlineVideothek::findUser(string name)
    

    Würdest du bisher nämlich Änderungen an dem zurückgegebenen Benutzer-Objekt machen (z.B. durch Aufruf von setCurrent_video), so wären diese Änderungen nicht im vector<Benutzer>-Objekt des OnlineVideothek-Objekts enthalten!

    Edit: In deinem case 6 hast du denselben Logik-Fehler bzgl. dem else wie in der login-Funktion. Überlege mal, was bisher passiert, wenn der 2. Wert der gefundene sein soll?



  • @Th69

    Benutzer& OnlineVideothek::findUser(string name) {
    
        for (unsigned int i = 0; i < this->users.size(); i++) {
    
            if (this->users.at(i).get_User() == name) {
    
                return this->users.at(i);
            } else {
    
                throw runtime_error("User nicht gefunden");
            }
        }
    // Hier müsste dann eine return funktion stehen
    }
    

    Jetzt erscheint auch die Warnung, dass meine Funktion das Ende einer non-void Funktion erreicht.
    Da wo mein Kommentar liegt, müsste ich da dementsprechend eine Exception schreiben?

    Bzgl. case 6: Die Funktion iteriert durch beide Vektoren, wenn erst der 2. Wert der gefundene sein soll, aber der erste Wert nicht der richtige ist, dann gibt er bereits beim 1. Wert ein false zurück und sucht gar nicht erst nach dem zweiten Wert, oder?



  • Jetzt überlege dir für diese Funktion mal, was passiert, wenn hier der 2. (oder 3. oder ...) Benutzer der zu findende ist?
    Und dann sollte dir dein genereller Logik-Fehler klar sein.



  • @Th69 Theoretisch sucht die Funktion erst gar nicht nach dem 2 oder 3 Benutzer, wenn bereits beim ersten Benutzer ein false zurückgegeben wird, richtig?



  • Ja, genau!



  • Das heißt man bräuchte keine Ausgabe von false, sondern er soll solange suchen, bis er den richtigen User gefunden hat und dann dementsprechend ein true zurückgeben. Also brauche ich in dem Fall kein false?



  • Ja, erst nach der Schleife gibst du false (oder eine Exception) zurück.



  • Super, habe ich geändert.
    Aber bzgl. der Rückgabe sagtest du, dass ich eine Exception werfen soll oder am besten eine konstante Referenz.

    Da müsstest du mir mal auf die Sprünge helfen. Wie würde das aus der Sicht der Syntax aussehen?



  • Das mußt dir dir ja selber überlegen, was du als Rückgabe dann verwenden willst. Bei einem Objekt oder einer (konstanten) Referenz müßtest du ja einen Standard-Benutzer (o.ä.) zurückgeben - und dann entsprechend nach dem Aufruf abfragen.
    Oder aber du nimmst einen Zeiger auf einen (konstanten) Benutzer und gibst dann nullptr zurück und fragst dementsprechend ab (andererseits sind Zeiger immer etwas unschöner zu benutzen, wegen der Dereferenzierung mittels *bzw. ->).
    Wenn du aber eher davon ausgehst, daß es eine Ausnahme ist, daß ein Benutzer nicht gefunden wird, dann würde ich die Variante mit der Exception nehmen.



  • @Th69 sagte in Vektor-Funktion bricht nach einer Eingabe ab:

    Das mußt dir dir ja selber überlegen, was du als Rückgabe dann verwenden willst. Bei einem Objekt oder einer (konstanten) Referenz müßtest du ja einen Standard-Benutzer (o.ä.) zurückgeben - und dann entsprechend nach dem Aufruf abfragen.
    Oder aber du nimmst einen Zeiger auf einen (konstanten) Benutzer und gibst dann nullptr zurück und fragst dementsprechend ab (andererseits sind Zeiger immer etwas unschöner zu benutzen, wegen der Dereferenzierung mittels *bzw. ->).
    Wenn du aber eher davon ausgehst, daß es eine Ausnahme ist, daß ein Benutzer nicht gefunden wird, dann würde ich die Variante mit der Exception nehmen.

    Hm, spannend. Ich habe eine andere Meinung. Für eine Funktion get_user(const string &name) - ok, die returnt einen User (Benutzer&) - fein. Aber wenn ich eine Funktion find_user habe, dann sehe ich schon im Namen, das da etwas gesucht wird, das auch mal nicht gefunden werden kann. Da würde ich grundsätzlich erwarten, dass hier irgendwas rauskommt, das ich testen muss, ähnlich wie bei std::find und Varianten. Ob es nun ein end-Iterator oder ein nullptr ist, egal.

    Daher würde ich

    Benutzer* OnlineVideothek::findUser(const string &name) {
        for (unsigned int i = 0; i < this->users.size(); i++) {
            if (this->users.at(i).get_User() == name)
                return &this->users.at(i);
        }
        return nullptr;
    }
    

    Wobei noch anzumerken wäre, dass ich die for-Schleife so nicht verwenden würde, aber das Beispiel jetzt nicht noch weiter ändern will. Die Frage ist auch, ob man nicht gleich eine map<Username, User> nehmen sollte. Im jetzigen Fall ist der Benutzer, der von dieser Funktion zurückkommt, nicht mehr gültig, wenn man eine andere Person einfügt.
    Und @Suprax, du solltest dringend den Namen der Funktion get_User überdenken! Der User ist doch schon das, was im Array users drin ist. Der sollte für den Namen eine Funktion wie get_name(), get_username() (oder ohne das get_) oder ähnlich haben! Außerdem ist es verwirrend, eine Klasse Benutzer zu haben, diesen aber mit createUser zu erstellen. Nimm einen Namen (den Englischen, wenn möglich).



  • Habs mittlerweile hinbekommen und die Fehler ausgebessert. Danke an alle!


Anmelden zum Antworten