Zeigerverwirrungen


darthcookie

Member
Joined
Aug 2, 2009
Messages
104
Hallo Leute, ich lern gerade C++ und verzweifle an den Zeigern. Ich hab mir eine Klasse geschrieben, die einen Dezimalbruch speichern soll (und möglichst viele Zeiger benutzt, ich wills ja lernen^^) und mir ein Programm zum testen geschrieben. Es müsste eigentlich 1/2 und 3/4 addieren, aber wenn ich mir die Werte ausgeben lasse ist der 1. Bruch (abc) 9/9, der 2. (def) 0/0 und der 3. (ghi) auch 9/9. (Bitte keinen Kommentar zu den Variablennamen, ist nur nen Testprogramm ;) ) Hier ist der Code
Code:
<i>
</i>#include <iostream>

using namespace std;

class Frac
{
	public:

		Frac(long num = 1, long denom = 1)
		{
		    numerator = new long(num);
            denominator = new long(denom);
        }

		~Frac()
		{
            delete numerator;
            delete denominator;
		}
		long getNum()
		{
            return *numerator;
        }
        long getDenom()
		{
            return *numerator;
        }
        void setNum(long num = 1)
        {
            numerator = &num;
        }
        void setDenom(long denom = 1)
        {
            if (denom != 0)
            {
                denominator = &denom;
            }
        }
        Frac* operator+(Frac sum)
        {
            *numerator = (*numerator * sum.getDenom())+(sum.getNum() * *denominator);
            *denominator *= sum.getDenom();
            return this;
        }

	protected:

	private:
        long *numerator;
        long *denominator;

};


int main()
{
    Frac *abc = new Frac(1,2);
    Frac *def = new Frac(3,4);
    Frac *ghi = new Frac();

    ghi = *abc + *def;


    cout << abc->getNum() << endl;
    cout << abc->getDenom() << endl;
    cout << def->getNum() << endl;
    cout << def->getDenom() << endl;
    cout << ghi->getNum() << endl;
    cout << ghi->getDenom() << endl;

    return 0;
}
 
Mir fallen zwei Probelmstellen sofort auf:

Warum sind deine Member Zeiger auf Long?

Im dem Operator "+" verändert Du die Membervariablen des Summanden abc, d. h. du arbeitest nicht mit einer Kopie von abc, sondern manipulierst abc direct. Als nächstes gibst Du einen Zeiger auf dem Summanden abc zurück und nicht auf ein neues Objekt. Daher sind die Werte von abc und ghi gleich. Zudem ist das instanzierte zuvor Objekt in ghi natürlich weg, da Du es ja mit dem Zeiger von abc überschreibst.

Gruß
Thomas
 
KidPaddle said:
Warum sind deine Member Zeiger auf Long?
Weil er üben möchte ;) Ist hier aber echt nicht angebracht, da unnötigerweise mit Pointern rum zu hantieren.
Code:
        long getDenom()
      {
            return *numerator;
        }
*möp* Bitte korrigieren ;)

Ansonsten ist es wie KidPaddle sagt. Mach am ende mal ein
Code:
<i>
</i>   cout << abc << endl << def << endl << ghi << endl;
dann sollte das direkt auffallen. Mit dem verlorenen ghi Objekt hast du auch dein erstes Speicherleck programmiert :p
Code:
<i>
</i>        Frac* operator+(Frac sum)
        {
            *numerator = (*numerator * sum.getDenom())+(sum.getNum() * *denominator);
            *denominator *= sum.getDenom();
            return this;
        }
Das ist übrigens recht "krank" ;)
Du kriegst zwei Objekte (*this und das andere als Value) und gibst einen Pointer auf die Adresse von dem ersten zurück. Wenn du wirklich keinen kuriosen Spezialfall hast, solltest du auch immer ein Objekt zurückgeben. Üblicherweise übergibt man das andere Objekt auch als const reference.
 
hmmmmm Jetzt hab ichs umgeschrieben, aber das Programm schmiert beim Addieren ganz ab (Prozess wurde mit Staus -1073741819 beended. (0 Minuten, 7 Sekunden) laut Code::Blocks)
Code:
<i>
</i>#include <iostream>

using namespace std;

class Frac
{
	public:

		Frac(long num = 1, long denom = 1)
		{
		    numerator = num;
            denominator = denom;
        }

		~Frac()
		{
            delete &numerator;
            delete &denominator;
		}
		long getNum()
		{
            return numerator;
        }
        long getDenom()
		{
            return denominator;
        }
        void setNum(long num = 1)
        {
            numerator = num;
        }
        void setDenom(long denom = 1)
        {
            if (denom != 0)
            {
                denominator = denom;
            }
        }
        Frac operator+(Frac sum)
        {
            
            Frac *fReturn = new Frac(numerator * sum.getDenom()+sum.getNum() * denominator, denominator * sum.getDenom());

            return *fReturn;
        }




	protected:

	private:
        long numerator;
        long denominator;

};


int main()
{
    

    Frac *abc = new Frac(1,2);

    

    Frac *def = new Frac(3,4);

    Frac ghi;

    cout << abc->getNum() << endl;
    cout << abc->getDenom() << endl;
    cout << def->getNum() << endl;
    cout << def->getDenom() << endl;

    ghi = *abc + *def;

    cout << ghi.getNum() << endl;
    cout << ghi.getDenom() << endl;

    
    return 0;

}
Es geht definitiv beim Addieren schief, da ich 4 Ausgaben habe^^ (die ersten 2 Brüche)
 
Zeit, debuggen zu lernen. Also mal schön im debugging modus starten und schritt für schritt ausführen. ansonsten kleistere alles mit cout << "Ich bin in Zeile " << __line__ << endl; zu ;)
Code:
<i>
</i>        Frac operator+(Frac sum)
        {
            
            Frac *fReturn = new Frac(numerator * sum.getDenom()+sum.getNum() * denominator, denominator * sum.getDenom());

            return *fReturn;
        }
Das wäre wieder ein Speicherleck. Du gibst ein Objekt zurück, und nicht einfach nur ein Objekt, sondern ein Kopie. Es wird hier also ein Objekt auf dem Heap erstellt. Der Zeiger fReturn zeigt drauf. Im return wird eine Kopie von dem Objekt erstellt, und die Variable fReturn die auf den Speicher zeigt, geht verloren, weil der Scope zu ende ist. Folglich zeigt nix mehr auf den Speicher und du kommst nicht mehr ran.
Was du machen kannst, das ist
Code:
<i>
</i>        Frac& operator+(Frac sum) //wir geben eine referenz zurück. 
        {
            
            Frac *fReturn = new Frac(numerator * sum.getDenom()+sum.getNum() * denominator, denominator * sum.getDenom());

            return *fReturn;
        }
, weil das referenzierte Objekt ja weiterhin im Speicher lebt. Aber schön ist das auch nicht.

Und klar das du aus dem Programm fliegst:
Code:
<i>
</i>     ~Frac()
      {
            delete &numerator;
            delete &denominator;
      }
delete wird nur auf adressen angewendet die du mit new erzeugt hast. Was soll er da denn löschen? :nope:
Sobald er das Frac Objekt abbauen soll, steigt er aus.

(Interessanterweise hatte ich heute das gleiche Problem in einem Quelltext auf der Arbeit gefunden)
 
Evil Azrael said:
Und klar das du aus dem Programm fliegst:
Code:
<i>
</i>     ~Frac()
      {
            delete &numerator;
            delete &denominator;
      }
delete wird nur auf adressen angewendet die du mit new erzeugt hast. Was soll er da denn löschen? :nope:
Sobald er das Frac Objekt abbauen soll, steigt er aus.

(Interessanterweise hatte ich heute das gleiche Problem in einem Quelltext auf der Arbeit gefunden)

Ups, das hab ich vergessen, als ich die die Member von long* in long umgeschrieben habe :dash:.
 
Back
Top