Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » C / C++ (ANSI-Standard) » Konstruktoren und Destruktoren bei geschachtelten Klassen

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: > 1 <
000
04.07.2008, 10:09 Uhr
~UweM
Gast


Einen schönen guten Tag,

ich habe ein Problem mit den Konstruktoren und Destruktoren bei geschachtelten Klassen und bin langsam am verzweifeln. Das Code-Beispiel liefert folgenden Ausdruck:

default-konstruktor von A beendet
spezi-konstruktor von A beendet 9
destruktor von A beendet 9
destruktor von A beendet 9
destruktor von A beendet 0

erwartet hätte ich aber:

default-konstruktor von A beendet
destruktor von A beendet 0
spezi-konstruktor von A beendet 9
destruktor von A beendet 9

Wo liegt mein Verständnisfehler? Na gut, das ist eine schwierige Frage.
Warum wird zweimal der destruktor mit id=9 aufgerufen? An dieser Stelle wird, glaube ich, wild im Speicher gelöscht und mein Programm geht hops.

Deklariere ich in B eine Pionter von A, dann läuft's so wie ich's gerne hätte. Aber warum oben nicht?

Vielen Dank


C++:
class A
{
    private:
        float *dat;
        int id;
    protected:
    public:
        A();
        A (int i ) ;
        ~A();
} ;

class B
{
    private:
        A *m_A;
    protected:
    public:
        B(){}
        B (int i ) ;
        ~B ();
} ;

A::A()
{
   id = 0;
   cout << " default-konstruktor von A beendet " << endl;
}

A::A(int i)
{
    id = i;
    dat = new float[i];
    cout << " spezi-konstruktor von A beendet " << id << endl;
}

A::~A()
{
    delete dat;
    cout << " destruktor von A beendet " << id << endl;
    id = 0;
}


B::B(int i)
{
    m_A = new A(i);
}

B::~B()
{
    m_A->~A();
}

int main(int argc, char* argv[])
{
    {

    B i_B(9);
    }
    getchar() ;
    return 0;
}

 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
001
04.07.2008, 10:12 Uhr
~UweM
Gast


Oh sorry, das war ja der Code mit dem Pointer. Der Code mit dem Problem ist der:


C++:

class A
{
    private:
        float *dat;
        int id;
    protected:
    public:
        A();
        A (int i ) ;
        ~A();
} ;

class B
{
    private:
        A m_A;
    protected:
    public:
        B(){}
        B (int i ) ;
        ~B ();
} ;

A::A()
{
   id = 0;
   cout << " default-konstruktor von A beendet " << endl;
}

A::A(int i)
{
    id = i;
    dat = new float[i];
    cout << " spezi-konstruktor von A beendet " << id << endl;
}

A::~A()
{
    delete dat;
    cout << " destruktor von A beendet " << id << endl;
    id = 0;
}


B::B(int i)
{
    m_A =  A(i);
}

B::~B()
{
    m_A.~A();
}

int main(int argc, char* argv[])
{
    {

    B i_B(9);
    }
    getchar() ;
    return 0;
}

 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
002
04.07.2008, 11:10 Uhr
ao

(Operator)



Zitat von ~UweM:


C++:
class B
{
    private:
        A m_A;
    public:
        B(){}
        B (int i ) ;
        ~B ();
} ;

A::A()
{
   id = 0;
   cout << " default-konstruktor von A beendet " << endl;
}

A::A(int i)
{
    id = i;
    dat = new float[i];
    cout << " spezi-konstruktor von A beendet " << id << endl;
}

A::~A()
{
    delete dat;
    cout << " destruktor von A beendet " << id << endl;
    id = 0;
}

B::B(int i)
{
    m_A =  A(i);
}

B::~B()
{
    m_A.~A();
}

int main(int argc, char* argv[])
{
    {

    B i_B(9);
    }
    getchar() ;
    return 0;
}


Das Code-Beispiel liefert folgenden Ausdruck:

default-konstruktor von A beendet
spezi-konstruktor von A beendet 9
destruktor von A beendet 9
destruktor von A beendet 9
destruktor von A beendet 0


Na, dann wolln wir mal gucken.

Die Zeile "B i_B(9);" bewirkt folgendes:

B::B(int i)
Konstruktion von B und Default-Konstruktion aller Member von B.
"default-konstruktor von A beendet"
{
m_A = A(i);
Konstruktion eines neuen A (mit Argument i)
"spezi-konstruktor von A beendet 9"

... und Kopieren der neuen Instanz in die Member-Variable m_A. Vorsicht, mehrere Fallen:

1. Da du keinen expliziten Kopier-Konstruktor geschrieben hast, wird vom Compiler einer generiert, der einfach nur elementweise kopiert. Das ist in Trivialfällen ausreichend, muss aber sehr genau beäugt werden, sobald Resourcen-Management im Spiel ist (dynamischer Speicher, offene Dateien, ...). Sonst referenzieren mehrere Instanzen dasselbe Resourcen-Objekt, und es kracht spätestens, wenn die Resource mehrfach freigegeben wird. Du musst einen sog. Kopier-Konstruktor schreiben, der sich drum kümmert, dass die Inhalte des Quellobjekts richtig herauskopiert werden.

2. Die Variable m_A enthält bereits ein Default-konstruiertes Objekt, das durch die Zuweisung verlorengeht. Auch hier: Bei Trivialobjekten kein Schaden, aber wenn das Objekt Resourcen festhält, können diese danach nicht mehr erreicht werden (Speicherleck, verlorene Dateien, ...). Das ist der Grund, warum du einen operator=() brauchst: Vor der Übernahme eines neuen Objekts muss das Zielobjekt alte Resourcen loslassen.

Richtig wäre die Verwendung einer sog. Initialisierungsliste:
B::B(int i)
: m_A (i)
{
}
Das würde ohne Zwischenschritte sofort das gewünschte A-Objekt erzeugen.

Weiter gehts an dieser Stelle:
}
mit dem Zerlegen des B-Objekts. Das geschieht automatisch an der schließenden Klammer, da der Lebensbereich des Objekts verlassen wird. ~B wird aufgerufen:

B::~B()
{
m_A.~A();
Dieser Aufruf gehört hier nicht hin. Es ist in 99,9 % der Fälle falsch, Destruktoren explizit aufzurufen (*). Entweder werden sie automatisch vom Compiler gerufen (wenn der Sichtbarkeitsbereich des Objekts verlassen wird), oder der Programmierer ruft sie mittelbar über "delete". Oder er vergisst es, das ist dann ein Leck.
(*) In der Microsoft-ATL gibt es Collection-Implementierungen, die ihre enthaltenen Kind-Objekte per ~T zerlegen. Ich habe auch sehr gestaunt, als ich das zum ersten Mal sah.
}
An dieser Stelle ruft der Compiler die Destruktoren aller Member von B, also auch den von m_A. Da du das vorher schon getan hast, wird es hier knallen.

Dieser Post wurde am 04.07.2008 um 11:11 Uhr von ao editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
003
04.07.2008, 11:31 Uhr
~UweM
Gast


Vielen Dank für die ausführliche Hilfe. Das ist genau das, was ich wissen wollte bzw. sollte.

Uwe
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
004
13.07.2008, 02:02 Uhr
xXx
Devil


Ehm nicht zu vergessen das da mal schön fehlt, dass nen array aufm heap mit new[] erzeugt, aber auch mit delete[] gelöscht wird!


C++:
class foo
{
    float* m_data;
    unsigned int m_size;
    
public:
    foo(const unsigned int size = 0)
        : m_data(new float[size]), m_size(size)
    {}

    ~foo() { delete [] m_data; }
};

class bar
{
    foo m_foo;

public:
    bar(const unsigned int size = 0)
        : m_foo(size)
    {}
};

int main()
{
    bar instance(9);
  
    std::cin.ignore();
}
Warum nicht einfach?
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: > 1 <     [ C / C++ (ANSI-Standard) ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: