Destruktor problem cpp



  • Hallo erst mal,
    ich sitze gerade an einer Hausarbeit/Laboraufgabe und ich habe irgendwo einen Fehler eingebaut und ich kann diesen nicht finden.
    Erst mal zur Aufgabe:

    Schreiben Sie eine Klasse 'Student' mit den Attributen '_name' und '_first_name' vom Typ String. Schützen Sie die Attribute gegen Zugriff von außen. Die folgenden Methoden implementieren sie außerhalb der Klasse: Die Klasse soll einen parametrisierten Konstruktor haben, der Name und Vorname entgegennimmt und die beiden Attribute '_name' und '_first_name' entsprechend belegt. Verwenden Sie hierzu keine Initialisierungsliste. Schreiben sie get-Methoden für beide Attribute und einen Destruktor. Schreiben Sie zwei print-Methoden, beide ohne Rückgabewert. Eine der beiden Methoden nimmt eine boolsche Variable entgegen, mit der gesteuert werden kann, ob am Ende der Zeile ein Zeilenumbruch erfolgen soll oder nicht. In der anderen Methode erfolgt immer ein Zeilenumbruch.

    Für die Klasse Employee soll ich genau die gleichen Methoden implementieren nur halt innerhalb der Klasse und mit einer Initialisierungsliste.

    Ich habe nun das Problem das irgendwie ein Destruktor aufgerufen wird, wo es gar kein Sinn ergibt.

    Einmal die gewollte Ausgabe:
    Parametrisierter Konstruktor Student: Max Mustermann
    Konvertierungskonstruktor Employee: Max Mustermann
    Parametrisierter Konstruktor Employee: Erika Mustermann
    Standardkonstruktor Employee: Erika Mustermann
    print() ohne Parameter; Student: Max Mustermann
    print() mit Parameter; Student: Max Mustermann
    print() ohne Parameter; Mitarbeiter: Max Mustermann
    print() ohne Parameter; Mitarbeiter: Erika Mustermann
    Block wird betreten
    Parametrisierter Konstruktor Student: Markus Mustermann
    print() mit Parameter; Student: Markus Mustermann
    Block wurde verlassen
    Destruktor Student: Markus Mustermann
    Destruktor Employee: Erika Mustermann
    Destruktor Employee: Max Mustermann
    Destruktor Student: Max Mustermann

    Und einmal meine Ausgabe:
    Parametrisierter Konstruktor Student: Max Mustermann
    Konvertierungskonstruktor Employee: Max Mustermann
    Destruktor Student: Max Mustermann <----- Hier liegt das problem
    Parametrisierter Konstruktor Employee: Erika Mustermann
    Standardkonstruktor Employee: Erika Mustermann
    print() ohne Parameter; Student: Max Mustermann
    print() mit Parameter; Student: Max Mustermann
    print() ohne Parameter; Mitarbeiter: Max Mustermann
    print() ohne Parameter; Mitarbeiter: Erika Mustermann
    Block wird betreten
    Parametrisierter Konstruktor Student: Markus Mustermann
    print() mit Parameter; Student: Markus Mustermann
    Block wurde verlassen
    Destruktor Student: Markus Mustermann
    Destruktor Employee: Erika Mustermann
    Destruktor Employee: Max Mustermann
    Destruktor Student: Max Mustermann

    
    #include <string>
    #include <iostream>
    using namespace std;
    
    // Klasse Student
    class Student{
        string _name;
        string _first_name;
    
        public:
        Student(string, string);
        string get_name();
        string get_first_name();
        ~Student();
        void print();
        void print(bool);
    };
    
    Student::Student(string name, string first_name){
        _name = name;
        _first_name = first_name;
        cout << "Parametrisierter Konstruktor Student: "<< _first_name << " " << _name << endl;
    }
    string Student::get_name(){
        return _name;
    }
    string Student::get_first_name(){
        return _first_name;
    }
    Student::~Student(){
        cout << "Destruktor Student: " << _first_name << " " << _name << endl;
    }
    void Student::print(){
        cout << "print() ohne Parameter; Student: " << _first_name << " " << _name << endl;
    }
    void Student::print(bool zeilenumbruch){
        if(zeilenumbruch == true){cout << "print() mit Parameter; Student: " << _first_name << " " << _name << endl;}
        if(zeilenumbruch == false){cout << "print() mit Parameter; Student: " <<  _first_name << " " << _name;}
    }
    
    
    
    // Klasse Employee
    class Employee{
        string _name;
        string _first_name;
    
        public:
    
        Employee(string name, string first_name)
            :_name(name),
            _first_name(first_name)
        {   
            cout << "Parametrisierter Konstruktor Employee: " << _first_name << " " << _name << endl; 
        }
        Employee() : Employee("Mustermann", "Erika"){
            cout << "Standardkonstruktor Employee: " << _first_name << " " << _name << endl; 
        }
        Employee(Student _student)
            :_name(_student.get_name()),
            _first_name(_student.get_first_name())
        {
            cout << "Konvertierungskonstruktor Employee: " << _first_name << " " << _name << endl;
        }
        ~Employee(){
            cout << "Destruktor Employee: " << _first_name << " " << _name << endl;
        }
        void print(){
            cout << "print() ohne Parameter; Mitarbeiter: " << _first_name << " " << _name << endl;
        }
        void print(bool zeilenumbruch){
            if(zeilenumbruch == true){cout << "print() mit Parameter; Mitarbeiter: " << _first_name << " " << _name << endl;}
            if(zeilenumbruch == false){cout << "print() mit Parameter; Mitarbeiter: " << _first_name << " " << _name;}
        }
    };
    
    int main(int argc, char *argv[])
    {
        Student stud_mustermann = Student("Mustermann", "Max");
        Employee empl_mustermann = Employee(stud_mustermann);
        Employee mit_default = Employee();
        stud_mustermann.print();
        stud_mustermann.print(true);
        empl_mustermann.print();
        mit_default.print();
        Student *p_stud_mustermann = nullptr;
        cout << "Block wird betreten" << endl;
        {
            p_stud_mustermann = new Student("Mustermann", "Markus");
            p_stud_mustermann->print(true);
        }
        cout << "Block wurde verlassen" << endl;
        delete p_stud_mustermann;
        return 0;
    }
    
    
    

    Vielen dank schon mal für`s Zeit nehmen und helfen.


  • Mod

    https://en.cppreference.com/w/cpp/language/rule_of_three

    Außerdem lass das mit dem new/delete einfach ganz sein.



  • Danke für die schnelle Antwort. Kann ich leider nicht, die main Funktion ist vorgeben und darf nicht modifiziert werden. Ich habe mir jetzt die Rule of three/five/zero durchgelesen, verstehe aber nicht so ganz wie mir das weiterhelfen kann.


  • Mod

    Da werden Kopien von deinen Objekten erzeugt, aber du bekommst davon nix mit, weil du das Kopieren nicht loggst. Diese Kopien werden am Ende ihrer Lebenszeit ganz normal über deinen Destruktor aufgeräumt, was du wiederum loggst.. Da mehr Instanzen deiner Klassen existieren als du mitbekommst, bekommst du mehr Destruktoraufrufe als du erwartest.

    Das ist erst einmal ganz normal und auch nicht schlimm, dass da an Stellen Kopien erzeugt werden (Es ist aber prinzipiell interessant unter welchen Umständen das warum passiert, aber gerade nicht das Thema). Da deine Klasse derzeit keine eigenen Ressourcen hält, geht da auch nix schief. Es ist nur eine Unzulänglichkeit in deinem Logging. Aber wenn deine Klasse jemals Ressourcen halten sollte (und wenn dein Lehrer ernsthaft solche new/delete Kombos zeigt, wird er sicher nicht die Rule of Zero kennen), dann wäre es falsch, aus den Gründen, die der Artikel erklärt.



  • Employee(Student _student)
            :_name(_student.get_name()),
            _first_name(_student.get_first_name())
        {
            cout << "Konvertierungskonstruktor Employee: " << _first_name << " " << _name << endl;
        }
    

    Schau hier in die erste Zeile deines Konvertierungskonstruktor. Der Parameter _student wird per KOPIE genommen. Das heißt, zum Aufrufen dieser Funktion wird der Student kopiert, auf der Kopie wird get_name und get_first_name aufgerufen und dann wird die Kopie wieder gelöscht, wenn der Employee-Konstruktor verlassen wird.

    Nimm stattdessen eine konstante Referenz als Paramter, also: Employee(const Student& student). (auch ohne _, denn den scheinst du doch für Member-Variablen zu nutzen)

    Aber Achung: deinen Funktionen wie get_name fehlt das const, um zu markieren, dass diese den Studenten nicht ändern. Statt

    class Student{
        string get_name();
    

    musst du schreiben:

    class Student{
        string get_name() const;
    

    if(zeilenumbruch == true)

    Ein Vergleich einer bool-Variablen mit true ergibt wenig Sinn. Einfach if (zeilenumbruch) ... und für den false-Fall if (!zeilenumbruch) ... (bzw. hier kannst du einfach else verwenden).



  • Super, danke euch beiden, ihr habt mir wirklich sehr geholfen.


Anmelden zum Antworten