Instanzen einer Klasse nutzen gleichen internen Zeiger



  • Hallo,

    ich habe folgende Klasse definiert:

    class turm
    {
        int *pturm;         			// Zeiger auf turm-Objekt
        int index;           		  	// turmindex, oberste Scheibe +1
        int hoehe;               	  	// Turmgrösse
    
      public:
        bool push(const int val);     	// Schreibe auf Turm ablegen
        int pop();                    	// Scheibe vom turm holen
        void druck(void);             	// Methode zur Ausgabe des Turms
        turm(int s=4);                	// Konstruktor, Standard-Turmgrösse 4
        ~turm();                      	// Destruktor (Speicher freigeben)
    };
    
    turm::turm(int s)
    {
        pturm = new int[s];				// Datenfeld reservieren
        index = 0;                    	// Index initialisieren
        hoehe = s;                    	// Höhe merken
    }
    

    Über den Konstruktor habe ich 3 Instanzen dieser Klasse erzeugt. Beim debuggen ist mir aufgefallen, dass der Pointer *pturm aller 3 Instanzen auf die gleiche Speicheradresse zeigen. Eigentlich sollte aber jede Instanz ihr eigenes Feld und einen eigenen Pointer haben. Was hab ich falsch gemacht? 😞
    Danke schon im Vorraus.

    Ich hab diese Frage in keinem anderen Forum gestellt.


  • Mod

    1. Wenn du einen nichttrivialen Destruktor hast, willst du fast immer auch einen eigenen Kopierkonstruktor und Zuweisungsoperator. So auch hier. Stichwort: Regel der großen Drei.
    2. Es gibt nie einen Grund, new[] einem vector vorzuziehen. So auch hier.
    3. Wenn du 2. anwendest, entfällt 1.



  • Zeig mal den Code, in dem du die Klasse instanziierst und diese Beobachtung machst. Übrigens fehlen Kopierkonstruktor und Kopier-Zuweisungsoperator, vielleicht ist es das ja schon.



  • Außerdem: Wenn Du einen Konstruktor mit einem Element aufrufen kannst und es handelt sich dabei nicht um den Kopierkonstukter, dann solltest Du mal darüber nachdenken, ob er nicht explizit sein sollte; denn sonst kann Dir folgendes passieren:

    void foo(turm);
    
    int main() {
      foo(9); // oops! das kompiliert ja! darf das so?
    }
    


  • Im Weiteren:

    • const int bringt in der Funktionsdeklaration von push() nichts, nimm einfach int .
    • void in Parameterlisten ist in C++ unüblich. Wenn du es dennoch machen willst, dann überall...
    • Benutze die Initialisierungsliste, um Member im Konstruktor zu initialisieren.


  • Wenn du einen Zeiger (pointergefummel ist übrigens nicht sexy) in deiner Klasse hast, wird nicht der Pointee sondern der Pointer selbst kopiert (wenn die Klasse kopiert wird). :xmas1:

    Edit: gerade stand da: zuletzt bearbeitet von Hacker ... . Ich hab überhaupt nichts bearbeitet.
    Edit²:
    Viel schöner wär da doch ein stack<int>, bzw. stack<Scheibe>.



  • Danke erstmal für die vielen Antworten. Ich hab meinen Code entsprechend überarbeitet.

    Vector und Stack fallen leider raus, da ich die Methoden pop und push selbst implementieren soll. Das ist auch alles fertig nur das Integerfeld macht mir Ärger.

    Kopierkonstruktor kannte ich vorher noch garnicht, bin noch recht neu in C++. Soweit ich das verstanden habe, ist der ja aber nur dafür da, ein Objekt vom Typ Turm in ein neues Objekt zu kopieren. Das will ich ja aber garnicht machen. Im Gegenteil ich möchte die möglichst getrennt haben. Brauch ich den troztdem?

    Bashar wollte ja noch die Initialisierung sehen:

    int main()
    {
        turm t1,t2,t3;                 		// Typdeklaration der Türme
        int anzahl,i;
    
        cin >> anzahl;
    
        t1=turm(anzahl);               		// initialisieren von Turm1
        t2=turm(anzahl);               		// initialisieren von Turm2
        t3=turm(anzahl);               		// initialisieren vonTurm3
    

  • Mod

    Kopierkonstruktor kannte ich vorher noch garnicht, bin noch recht neu in C++. Soweit ich das verstanden habe, ist der ja aber nur dafür da, ein Objekt vom Typ Turm in ein neues Objekt zu kopieren. Das will ich ja aber garnicht machen.

    Doch, den Kopierkonstruktor brauchst du ganz oft, du siehst es nur meistens nicht. Was meinst du, was zum Beispiel passiert, wenn du ein Objekt an eine Funktion übergibst? Ebenso der Zuweisungsoperator, den du sogar direkt 3x benutzt.

    Die folgenden drei Aussagen können nicht gleichzeitig wahr sein:
    1.

    Mephi schrieb:

    da ich die Methoden pop und push selbst implementieren soll.

    2.

    Kopierkonstruktor kannte ich vorher noch garnicht

    3. Das wird was.

    Sicher, dass du deine Aufgabe richtig verstanden hast?



  • Mephi schrieb:

    Bashar wollte ja noch die Initialisierung sehen:

    t1=turm(anzahl);               		// initialisieren von Turm1
    

    Das ist keine Initialisierung, sondern eine Zuweisung.

    Zuerst wird der Ausdruck auf der rechten Seite ausgewertet, das ist die Erzeugung eines temporären Objektes mithilfe des Konstruktors. Dann wird dieses an das Objekt auf der linken Seite zugewiesen. Dazu wird der Zuweisungsoperator benutzt. Da du den nicht implementiert hast, bastelt sich der Compiler selber einen, der einfach nur die Member des Objektes einzeln zuweist -- auch den Zeiger. Dann wird das temporäre Objekt zerstört.

    Das ganze wiederholt sich noch zweimal. Da das temporäre Objekt jeweils zerstört wird, ist es ganz plausibel, dass bei der nächsten Erzeugung im Konstruktor das new wieder den gleichen Zeiger wie beim letzten mal zurückliefert.

    Am Ende müsste das Programm abstürzen, weil deine turm-Variablen ja auch zerstört werden und immer wieder den gleichen Zeiger deleten.



  • @Bashar: Nach deiner Erklärung macht es noch weniger Sinn, dass alle drei Türme den gleichen Zeiger bekommen. Die anderen Elemente der Klasse werden auch separat initialisiert und überschreiben sich nicht gegenseitig.

    @Sepp: Kannst du mir bei dem Kopierkontruktor helfen? Ich würde gerne testen, ob der das Problem löst. Ich hab verschiedene Notationen probiert, aber bekomm die anzahl nicht übergeben. 😕



  • Mit Probieren alleine kommst Du in C++ nicht weit!

    Mephi, wenn Du da links vom Gleichheitszeichen schon ein „fertiges“ Objekt und rechts sowas wie Typ(parameter) stehen hast, dann wird wegen der rechten Seite ein TEMPORÄRES Typ-Objekt erzeugt/initialisiert und dessen Elemente elementweise in das Objekt auf der linken Seite kopiert (da Du ja keinen eigenen Zuweisungsoperator definierst). „Elemente“ bezieht sich auf pturm, index und hoehe in deinem Fall. Du hast also zwischendurch zwei Turm-Objekte, die auf dasselbe Array zeigen. Ein temporäres und t1. Danach wird das temporäre Objekt zerstört (quasi beim Semikolon). Dessen Destruktor gibt aber das Array wieder frei und t1 zeigt auf ungültigen Speicher! Das ist eine klassische Verletzung der Dreierregel. Wenn Du Türme sowieso nicht kopieren und zuweisen willst, kannst Du das auch explizit verbieten, statt dem Compiler solche Operationen zu überlassen:

    class Turm
    {
    private:
      Turm(Turm const&); // copy ctor
      Turm& operator=(Turm const&); // copy assign
      …
    public:
      …
    }
    

    Copy ctor und copy assign werden privat deklariert und bleiben undefiniert. Solltest Du dann versehentlich versuchen ein Turm-Objekt zu kopieren oder einem anderen zuzuweisen, wird der Compiler (spätestens der Linker) Dir das anmeckern…

    Hast Du schon Erfahrungen mit anderen Programmiersprachen, die Dich hier durcheinander bringen und bei Dir falsche Erwartungen erzeugen?



  • Drüber nachgedacht -> gegrübelt -> probiert -> hinbekommen -> funktioniert

    ... eigentlich ganz einfach wenn mans erstmal kapiert hat ... so schwer war das jetzt mit dem kopierkonstruktor garnicht -.-' *shy*


Anmelden zum Antworten