Destructor, Copy Constructor und ein paar weitere Probleme



  • Hey!

    Ich habe grade arge Probleme mit einer Aufgabe 😕 . Wir sollen eine Klasse erstellen und ihr einen Destruktor, einen Copy-Constructor (mit überlagertem Zuweisungsoperator), sowie eine Kontrolle für Parameter von nicht-privaten Variablen auf deren Legalität verpassen.

    Mein Code sieht bisher wie folgt aus:

    Header:

    using namespace std;
    
    class ClZeichenkette
       {
    private:
       char *text;
       int laenge;
       void addiere ( const ClZeichenkette &zk1, const ClZeichenkette &zk2 );
       void eingeben ( istream &eingabe);
    
    public:
    
       ClZeichenkette() {laenge = 0; text = NULL;}
       ClZeichenkette (const char* start);
       ClZeichenkette & operator=(const ClZeichenkette & zk);
       ~ClZeichenkette();
    
       friend ostream & operator << (ostream &ausgabe, ClZeichenkette &zk)
          {
          ausgabe << zk.text;
          return ausgabe;
          }
       friend istream & operator >> (istream &eingabe, ClZeichenkette &zk)
          {
          zk.eingeben(eingabe);
          return eingabe;
          }
        ClZeichenkette operator+ (const ClZeichenkette & zk2)
          {
          ClZeichenkette ketteneu;
          ketteneu.addiere(*this, zk2);
          return ketteneu;
          }
       } ;
    

    Library:

    #include <iostream>
    #include <string>
    #include "zeichenkette.h"
    
    using namespace std;
    
    ClZeichenkette::ClZeichenkette(const char* start)
    {
    laenge = strlen(start);
    text = new char[laenge+1];
    strcpy(text,start);
    }
    
    ClZeichenkette & ClZeichenkette::operator=(
    const ClZeichenkette   &zk)
    {
    if (this == &zk)
       return *this;
    
    if (text!=NULL)
       {
       delete text;
       text=NULL;
       }
    
    text=new char[laenge+1];
    strcpy(text,zk.text);
    
    return *this;
    }
    
    ClZeichenkette::~ClZeichenkette()
    {
    	delete text;
    }
    
    void ClZeichenkette::addiere(const ClZeichenkette &zk1, const ClZeichenkette &zk2)
    {
    laenge = strlen(zk1.text)+strlen(zk2.text);
    text = new char[laenge+1];
    strcpy(text,zk1.text);
    strcat(text,zk2.text);
    }
    
    void ClZeichenkette::eingeben(istream &eingabe)
    {
    int zaehler;
    char puffer[500];
    
    for (zaehler=0;zaehler<500;zaehler++)
        {
        eingabe.get(puffer[zaehler]);
        if (puffer[zaehler] == '\n') break;
        }
    puffer[zaehler]='\0';
    
    text = new char[zaehler+1];
    strcpy(text,puffer);
    laenge = zaehler;
    }
    

    Hauptprogramm:

    #include <iostream>
    using namespace std;
    #include "zeichenkette.h"
    
    int main()
    {
    ClZeichenkette zk1, zk2, zk3;
    
    cout << "Bitte geben Sie eine beliebig lange erste Zeichenkette ein:";
    cin >> zk1;
    cout << "Bitte geben Sie eine beliebig lange zweite Zeichenkette ein:";
    cin >> zk2;
    
    zk3 = zk1 + zk2;
    
    cout << zk1 << " + " << zk2 << " = " << zk3 << endl;
    
    int x;
    cin >> x;
    }
    

    So funktioniert es leider noch nicht 👎 . An die Kontrolle der Parameter hab ich mich noch gar nicht rangewagt 😞 Wäre ja schön wenn die anderen 2 Sachen erstmal richtig funktionieren. 🤡

    Wäre total lieb wenn ihr mir helfen könnt.

    LG,
    Sarah



  • Wie äußert sich denn dieses "funktioniert nicht"?

    Was mir auf Anhieb auffällt:
    - du hast noch keinen Copy-Ctor in deiner Klasse (und die Default-Version des Compilers macht dir deine Speicherverwaltung kaputt)
    - addiere() gibt den bisher verwendeten Speicher nicht wieder frei
    - eingeben() könnte über das Ende des bereitgestellten Hilfsspeichers hinausschreiben
    - und im Zuweisungsoperator solltest du auch die Länge des Quellstrings übernehmen



  • Hmm, also ich kann die 2 Strings eingeben. Das geht. Aber anstatt dann

    cout << zk1 << " + " << zk2 << " = " << zk3 << endl;
    

    auszugeben, kommt ein Fehler. "Expression: _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)"

    Der Speicher soll ja durch den Destructor wieder freigegeben werden, indem alle "text" zerstört werden. Leider passiert das scheinbar schon vor der Ausgabe 😞
    Über den Hilfsspeicher wird nicht hinausgeschrieben. Da gib ich sicher, da ich immer nur sehr kurze Zeichenketten eingebe.
    Und die Länge des Quellstring ist doch auch da [laenge+1] 😕
    Tschuldige wenn ich mich etwas doof anstelle. Lerne c++ erst seit 3 Monaten.

    LG,Sarah



  • Wie gesagt, macht dir der compilergenerierte Kopier-Konstruktor einen Strich durch die Rechnung. Dieser wird bei der Rückgabe aus dem operator+ aufgerufen und kopiert die Inhalte des Objekts 1:1. Das heißt, kurz vor Ende der Anweisung zk3=zk1+zk2; hast du zwei Objekte, die auf die selben Daten verweisen (zk3 und das lokale ketteneu). Danach wird ketteneu zerstört, löscht die Daten und lässt auf diese Weise zk3 mit einem Zeiger in einen freigegebenen Speicherbereich zurück.

    Sarah83 schrieb:

    Und die Länge des Quellstring ist doch auch da [laenge+1] 😕

    Ja, aber der Quellstring hat seine eigene Länge, die sich von der des Zielstrings unterscheiden könnte:

    ClZeichenkette s1="Hallo"; //laenge=5, text zeigt auf ein char[6]
    ClZeichenkette s2;         //laenge=0, text=NULL
    s2 = s1;//Zuweisung -> s2 reserviert einen char[1] und will dort 6 Zeichen reinpacken
    


  • Okay, also das Problem mit der Länge des Quellstrings löst sich doch, in dem ich aus

    text=new char[laenge+1];
    strcpy(text,zk.text);
    
    return *this;
    

    ein:

    text=new char[zk.laenge+1];
    strcpy(text,zk.text);
    
    return *this;
    

    mach, oder? Also zk.laenge, das ist dann ja der Quellstring.

    Kannst du mir vielleicht einen Tipp geben was ich machen muss, damit das mit dem Destruktor anständig funktioniert? Du sagst ja dass da ein Konflikt mit dem überladenen + operator entseht, wenn ich das richtig verstehe.

    Wenn ich den Detsruktor "aktiviere", dann kriege ich als Ergebnis jetzt zumindest schon mal ne Reihe kryptischer Zeichen und keinen Error mehr.
    Das wäre super lieb.

    LG,
    Sarah 🤡



  • Hallo Sarah,

    du mußt noch den Copy-Konstruktor selber erzeugen und dort dann eine Kopie deines internen Textes anlegen (ähnlich wie beim =-Operator):

    ClZeichenkette(const ClZeichenkette & zk)
    {
      ...
    }
    

    Wie CStoll schon geschrieben hat, erzeugt der Compiler sonst selber einen Default-Copy-Konstruktor, der einfach nur die internen Member kopiert (d.h. 'text' und 'laenge').

    Dies besagt auch die Regel der großen Drei.



  • Aber ich dachte das habe ich schon hiermit erledigt:

    ClZeichenkette & ClZeichenkette::operator=(
    const ClZeichenkette   &zk)
    {
    if (this == &zk)
       return *this;
    
    if (text!=NULL)
       {
       delete text;
       text=NULL;
       }
    
    text=new char[zk.laenge+1];
    strcpy(text,zk.text);
    
    return *this;
    }
    

    😕



  • Nein das ist der Zuweisungsoperator!



  • Okay, ich habe das jetzt noch gemacht. Ich poste noch mal meinen Code wie er jetzt aussieht:

    Header:

    using namespace std;
    
    class ClZeichenkette
       {
    private:
       char *text;
       int laenge;
       void addiere ( const ClZeichenkette &zk1, const ClZeichenkette &zk2 );
       void eingeben ( istream &eingabe);
    
    public:
       char *kette() { return text; }
       int anzahl() { return laenge; }
       ClZeichenkette() {laenge = 0; text = NULL;}
       ClZeichenkette (const char* start);
       ClZeichenkette & operator=(const ClZeichenkette & zk);
       ClZeichenkette(const ClZeichenkette & zk);
       ~ClZeichenkette();
       friend ostream & operator << (ostream &ausgabe, ClZeichenkette &zk)
          {
          ausgabe << zk.text;
          return ausgabe;
          }
       friend istream & operator >> (istream &eingabe, ClZeichenkette &zk)
          {
          zk.eingeben(eingabe);
          return eingabe;
          }
        ClZeichenkette operator+ (const ClZeichenkette & zk2)
          {
          ClZeichenkette ketteneu;
          ketteneu.addiere(*this, zk2);
          return ketteneu;
          }
       } ;
    

    Library:

    #include <iostream>
    #include <string>
    #include "zeichenkette.h"
    
    using namespace std;
    
    ClZeichenkette::ClZeichenkette(const char* start)
    {
    laenge = strlen(start);
    text = new char[laenge+1];
    strcpy(text,start);
    }
    
    ClZeichenkette & ClZeichenkette::operator=(
    const ClZeichenkette   &zk)
    {
    if (this == &zk)
       return *this;
    
    delete text;
    text=new char[zk.laenge+1];
    strcpy(text,zk.text);
    
    return *this;
    }
    
    ClZeichenkette::ClZeichenkette(const ClZeichenkette & zk)
    {
    text=new char[zk.laenge+1];
    strcpy(text,zk.text);
    }
    
    ClZeichenkette::~ClZeichenkette()
    {
    	delete text;
    }
    
    void ClZeichenkette::addiere(const ClZeichenkette &zk1, const ClZeichenkette &zk2)
    {
    laenge = strlen(zk1.text)+strlen(zk2.text);
    text = new char[laenge+1];
    strcpy(text,zk1.text);
    strcat(text,zk2.text);
    }
    
    void ClZeichenkette::eingeben(istream &eingabe)
    {
    int zaehler;
    char puffer[500];
    
    for (zaehler=0;zaehler<500;zaehler++)
        {
        eingabe.get(puffer[zaehler]);
        if (puffer[zaehler] == '\n') break;
        }
    puffer[zaehler]='\0';
    
    text = new char[zaehler+1];
    strcpy(text,puffer);
    laenge = zaehler;
    }
    

    Hauptprogramm:

    #include <iostream>
    using namespace std;
    #include "zeichenkette.h"
    
    int main()
    {
    ClZeichenkette zk1, zk2, zk3;
    
    cout << "Bitte geben Sie eine beliebig lange erste Zeichenkette ein:";
    cin >> zk1;
    cout << "Bitte geben Sie eine beliebig lange zweite Zeichenkette ein:";
    cin >> zk2;
    
    zk3 = zk1 + zk2;
    
    /* Soll so implementiert werden, daß '+' als 'Verkettung' definiert ist.
       D.h.: Aus den Ketten 'abc' + 'def' wird: 'abcdef' */
    
    cout << zk1 << " + " << zk2 << " = " << zk3 << endl;
    
    int x;
    cin >> x;
    }
    

    Wenn ich jetzt komppilieren, bekomme ich wieder einen Fehler:

    R6010:
    - abort() has been called

    😡



  • Du hast bei beiden Kopier-Operationen (Copy-Ctor und Zuweisung) vergessen, die Länge des Zielstrings an die neuen Gegebenheiten anzupassen.
    Außerdem ist das Gegenstück zu p = new char[...]; nicht delete p; , sondern [c]delete**[]** p;[/c]

    (über die Effizienz deiner Funktionien sage ich jetzt mal nichts, darum können wir uns kümmern, wenn du die Grundlagen verstanden hast)



  • CStoll schrieb:

    Du hast bei beiden Kopier-Operationen (Copy-Ctor und Zuweisung) vergessen, die Länge des Zielstrings an die neuen Gegebenheiten anzupassen.

    Das tue ich doch, indem ich die Länge auf zk.text setze, oder was meinst du jetzt genau? Sorry wenn ich so viele Fragen habe!
    🙄



  • Dort fehlt jeweils noch eine Zuweisung laenge = zk.laenge; , damit der zugewiesene String hinterher weiß, wie lang er tatsächlich ist.



  • jetzt funktioniert es. danke 👍


Anmelden zum Antworten