C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Warum Absturz?

 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ (German)
View previous topic :: View next topic  
Author Message
Falco Meurer
Guest





PostPosted: Sat Oct 16, 2004 2:34 pm    Post subject: Warum Absturz? Reply with quote



Hallo,

folgender Programmteil stürzt bei mir ab, und ich suche schon seit Stunden
nach dem tatsächlichen Grund:

**

#include <iostream>

class Path
{
public:
int* way;
int n;
Path(int num);
~Path();
};

Path::Path(int num)
{
n = num;
way = new int[num];
}

Path::~Path()
{
delete [] way;
}

class FTreeNode
{
public:
FTreeNode() {sibling=NULL;}
FTreeNode* sibling;
};

class FTree
{
public:
FTree() {RootNode=NULL;}
FTreeNode* GetNodeFromPath(Path p);
FTreeNode* AddNode(Path p);
FTreeNode* RootNode;
};

FTreeNode* FTree::GetNodeFromPath(Path p)
{
return RootNode;
}

FTreeNode* FTree::AddNode(Path p)
{
FTreeNode* temp = GetNodeFromPath(p);
while (temp->sibling != NULL) temp = temp->sibling;
temp->sibling = new FTreeNode;
return temp->sibling;
}

int main(int argc, char *argv[])
{
FTree Tree;
Tree.RootNode = new FTreeNode;
for (int x = 0; x < 1000; x++) {
printf("%d ", Tree.AddNode( Path(0) ) );
}

system("PAUSE");
return 0;
}

**

Ich vermute, dass es etwas mit der Konstruktion der Klasse "Path" zu tun
haben muss, denn wenn ich in Konstruktor/Destruktor die Variable "way" nicht
mit new initialisiere, kommt es zu keinem Absturz.
Oder, wenn ich in der Funktion "AddNode" den Aufruf "GetNodeFromPath(p)"
direkt mit "RootNode" ersetze, passiert auch nichts.
Hat es vielleicht etwas mit Kopierkonstruktor zu tun?

Danke im Voraus,
Falco

--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de
Back to top
Thomas Mang
Guest





PostPosted: Sat Oct 16, 2004 6:14 pm    Post subject: Re: Warum Absturz? Reply with quote




"Falco Meurer" <falco.meurer (AT) fernuni-hagen (DOT) de> schrieb im Newsbeitrag
news:ckrbbi$6gl$1 (AT) beech (DOT) fernuni-hagen.de...
Quote:
Hallo,

folgender Programmteil stürzt bei mir ab, und ich suche schon seit Stunden
nach dem tatsächlichen Grund:


**

#include <iostream

class Path
{
public:
int* way;
int n;
Path(int num);
~Path();
};

warum sind die Datenmember nicht private?


Quote:

Path::Path(int num)
{
n = num;
way = new int[num];
}

Warum verwendest Du nicht die Initialisierungsliste?

Quote:

Path::~Path()
{
delete [] way;
}


Bei deiner Klasse machen der Compiler-generierte Kopierkonstruktor und
operator= was falsches, nämlich eine direkte Kopie/Zuweisung der
Datenmitglieder.
Im Fall von n macht das nichts, bei way ist es schlimm.


Stell Dir folgendes vor:

Path a(100);
Path b(a);


Jetzt alloziiert der Konstruktor von a 100 ints. Fein. Dann wird b als Kopie
von a erstellt. b.n ist dann ebenfalls gleich 100, aber b.way zeigt auf
dieselbe Adresse wie a.way. Zwei Zeiger, die auf die gleiche Adresse zeigen.
Soweit passiert noch nichts. Aber.....
Jetzt werden die Destruktoren aufgerufen, und zwar in umgekehrter
Reihenfolge. Zuerst wird b zerstört und der Speicher, auf den b.way zeigt
freigegeben. Dann wird a zerstört, und sein Destruktor ruft ebenfalls den
operator delete[] für way auf. Doch dieser Speicher wurde bereits
freigegeben, nämlich im Destruktor von b (da ja beide Zeiger auf dieselbe
Adresse zeigen). Deshalb krachts.

Die Lösung ist

a) Eine "tiefe Kopie", das heißt Du schreibst Deinen eigenen
Kopierkonstruktor / operator=, wo way auf neu alloziierten Speicher gesetzt
wird und die ints hineinkopiert werden

b) Einfacher: Du verwendest std::vector operator= das richtige machen. In dem Fall brauchst Du für Deine Klasse
keinen eigenen Kopierkonstruktor / operator= schreiben, da die
Compiler-generierten Versionen das richtige machen.

Quote:

class FTreeNode
{
public:
FTreeNode() {sibling=NULL;}

a) Initialisierungsliste verwenden.
b) vermeide NULL. Das verwendet man in C, da dort die
pointer-Umwandlung-Semantik anders ist. In C++ verwendet man 0.

Quote:
FTreeNode* sibling;
};

Wieder: Datenmember sollten private sein.



Quote:

class FTree
{
public:
FTree() {RootNode=NULL;}

Wie oben.


Quote:
FTreeNode* GetNodeFromPath(Path p);
FTreeNode* AddNode(Path p);

Warum wird Path nicht per const-Referenz übergeben?


Quote:
FTreeNode* RootNode;
};

FTreeNode* FTree::GetNodeFromPath(Path p)
{
return RootNode;
}

Du verwendest den Parameter p gar nicht. Dann entferne ihn. Außerdem scheint
mir die Funktion, gemessen an dem was sie macht, nicht den passenden Namen
zu tragen.


Quote:

FTreeNode* FTree::AddNode(Path p)

Da GetNodeFromPath nun keinen Path mehr als Parameter empfängt, braucht
diese Funktion auch keinen mehr.

Quote:
{
FTreeNode* temp = GetNodeFromPath(p);
while (temp->sibling != NULL) temp = temp->sibling;
temp->sibling = new FTreeNode;
return temp->sibling;
}


Aha, eine einfach verkettete Liste.

Dann braucht Deine Klasse aber auch einen Destruktor, der die einzelnen
Knoten wieder freigibt.

Außerdem fehlt ein Konstruktor, der RootNode auf 0 stellt. Dann mußt Du
logischerweise noch Deine while-Bedingung so anpassen, daß RootNode 0 sein
kann.

Und selbstverständlich mußt Du eigene Versionen vom Kopierkonstruktor /
operator= schreiben, da sonst dasselbe Problem auftritt wie bei Path.


Quote:

int main(int argc, char *argv[])
{
FTree Tree;
Tree.RootNode = new FTreeNode;
for (int x = 0; x < 1000; x++) {
printf("%d ", Tree.AddNode( Path(0) ) );
}

a) man sollte die prä-Inkrement Version der Post-Inkrement vorziehen.
b) printf ist C. In C++ verwendet man std::cout und operator<<.

Quote:

system("PAUSE");
return 0;
}

**

Ich vermute, dass es etwas mit der Konstruktion der Klasse "Path" zu tun
haben muss, denn wenn ich in Konstruktor/Destruktor die Variable "way"
nicht
mit new initialisiere, kommt es zu keinem Absturz.
Oder, wenn ich in der Funktion "AddNode" den Aufruf "GetNodeFromPath(p)"
direkt mit "RootNode" ersetze, passiert auch nichts.
Hat es vielleicht etwas mit Kopierkonstruktor zu tun?

Ja, mit dem Kopierkonstruktor von Path.



Noch zwei Bemerkungen:

Dein aktuelles Design ist extrem fehleranfällig für Benutzer. Bitte, lerne
und beherrsche "encapsulation".

Es gibt in der Standardbibliothek Container, z.B. std::list. Die machen
genau das, was Du nachbastelst, nur sicher effizienter, fehlerfrei (sollte
man doch zumindest hoffen) und (hoffe ich ebenfalls) exception-sicher.


Thomas

--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de

Back to top
Thomas Maeder
Guest





PostPosted: Sat Oct 16, 2004 6:22 pm    Post subject: Re: Warum Absturz? Reply with quote



"Falco Meurer" <falco.meurer (AT) fernuni-hagen (DOT) de> writes:

Quote:
folgender Programmteil stürzt bei mir ab, und ich suche schon seit Stunden
nach dem tatsächlichen Grund:

Ich sehe verschiedene Gründe.


Quote:
#include <iostream

class Path
{
public:
int* way;
int n;
Path(int num);
~Path();
};

1. public data members


Quote:
Path::Path(int num)
{
n = num;
way = new int[num];
}

[Für die Initialisierung von Members gibt es in C++ die
Memberinitialisierungsliste.]


Quote:
Path::~Path()
{
delete [] way;
}

2. Kopierkonstruktor und Kopierzuweisungsoperator der Klasse Path verhalten
sich nicht so, wie es der oben gezeigte Konstruktor und dieser Konstruktor
verlangen. Sie kopieren Member für Member, und spätestens bei der Destruktion
einer von mehreren Kopien hat das Programm undefiniertes Verhalten.

-> private deklarieren und/oder richtig implementieren.


Quote:
class FTreeNode
{
public:
FTreeNode() {sibling=NULL;}

[Für die Initialisierung von Members gibt es in C++ die
Memberinitialisierungsliste.]


Quote:
FTreeNode* sibling;

3. public data members


Quote:
};

class FTree
{
public:
FTree() {RootNode=NULL;}

[Für die Initialisierung von Members gibt es in C++ die
Memberinitialisierungsliste.]

Quote:
FTreeNode* GetNodeFromPath(Path p);
FTreeNode* AddNode(Path p);
FTreeNode* RootNode;

4. public data members


Quote:
};

FTreeNode* FTree::GetNodeFromPath(Path p)
{
return RootNode;
}

[Wozu dient der Parameter p?]


Quote:
FTreeNode* FTree::AddNode(Path p)
{
FTreeNode* temp = GetNodeFromPath(p);
while (temp->sibling != NULL) temp = temp->sibling;
temp->sibling = new FTreeNode;
return temp->sibling;
}

[Wozu dient der Parameter p?]

--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de

Back to top
Thomas Mang
Guest





PostPosted: Sat Oct 16, 2004 8:37 pm    Post subject: Re: Warum Absturz? Reply with quote


"Thomas Mang" <nospam (AT) nospam (DOT) prima.de> schrieb im Newsbeitrag
news:41716517$0$12646$3b214f66 (AT) usenet (DOT) univie.ac.at...
Quote:



class FTreeNode
{
public:
FTreeNode() {sibling=NULL;}

a) Initialisierungsliste verwenden.
b) vermeide NULL. Das verwendet man in C, da dort die
pointer-Umwandlung-Semantik anders ist. In C++ verwendet man 0.


Ich glaube, da habe ich mich unklar ausgedrückt:

Laut aktuellem Standard kann man durchaus NULL verwenden, da der eine
Definition a là

#define NULL (void*) 0

nicht zuläßt, sondern jeden null-pointer repräsentieren muß. Die
unterschiedliche Umwandlungssemantik zwischen C und C++ spielt theoretisch
keine Rolle.

Das Problem ist (bzw. besser war) eher praxisrelevant, da nicht jeder
Compiler sich daran hielt.


Thomas

--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de

Back to top
Horst Kraemer
Guest





PostPosted: Sun Oct 17, 2004 8:53 am    Post subject: Re: Warum Absturz? Reply with quote

"Falco Meurer" <falco.meurer (AT) fernuni-hagen (DOT) de> wrote:

Quote:
Hallo,

folgender Programmteil stürzt bei mir ab, und ich suche schon seit Stunden
nach dem tatsächlichen Grund:

**

#include <iostream

class Path
{
public:
int* way;
int n;
Path(int num);
~Path();
};

Path::Path(int num)
{
n = num;
way = new int[num];
}

Path::~Path()
{
delete [] way;
}

class FTreeNode
{
public:
FTreeNode() {sibling=NULL;}
FTreeNode* sibling;
};

class FTree
{
public:
FTree() {RootNode=NULL;}
FTreeNode* GetNodeFromPath(Path p);
FTreeNode* AddNode(Path p);
FTreeNode* RootNode;
};

FTreeNode* FTree::GetNodeFromPath(Path p)
{
return RootNode;
}

FTreeNode* FTree::AddNode(Path p)
{
FTreeNode* temp = GetNodeFromPath(p);
while (temp->sibling != NULL) temp = temp->sibling;
temp->sibling = new FTreeNode;
return temp->sibling;
}

int main(int argc, char *argv[])
{
FTree Tree;
Tree.RootNode = new FTreeNode;
for (int x = 0; x < 1000; x++) {
printf("%d ", Tree.AddNode( Path(0) ) );
}

system("PAUSE");
return 0;
}

**

Ich vermute, dass es etwas mit der Konstruktion der Klasse "Path" zu tun
haben muss, denn wenn ich in Konstruktor/Destruktor die Variable "way" nicht
mit new initialisiere, kommt es zu keinem Absturz.
Oder, wenn ich in der Funktion "AddNode" den Aufruf "GetNodeFromPath(p)"
direkt mit "RootNode" ersetze, passiert auch nichts.
Hat es vielleicht etwas mit Kopierkonstruktor zu tun?

Richtig analysiert.

Du rufst AddNode mit einer Parameter vom Typ Path auf, der per Path(0)
im Moment des Aufrufs generiert wird, dieses Path-Objekt wird dann
innerhalb von AddNode an GetNodeFromPath per *Kopie* weitergereicht.
Diese beiden Prameter-Instanzen von Path werden nun jeweils beim
Verlassen ihrer Funktion per ~Path zerstoert. Der
Default-Kopierkonstruktor kopiert aber nur bloed die Werte der
Elemente. D.h. der Zeiger way der Kopie von Path(0) wird direkt in der
Kopie fuer den Parameter von GetNodeFromPath uebernommen. Daher zeigen
beide auf dieselben Objekte und das Zerstoeren des AddNode-Parameters
gibt den bereits beim Zerstoeren des GetNodeFromPath freigegebenen
Speicherplatz erneut frei - was zu undefiniertem Verhalten fuehrt.

Du kannst dieses Problem zu Demonstrationszwecken beheben, wenn Du

FTree::GetNodeFromPath

nicht so

FTreeNode* FTree::GetNodeFromPath(Path p)

sondern so

FTreeNode* FTree::GetNodeFromPath(const Path &p)

deklarierst. Dann wird nicht eine Kopie des AddNode-Parameters,
sondern nur eine Referenz auf diesen an GetNodeFromPath uebergeben,
d.h. es wird physisch *dasselbe* Path-Objekt weitergereicht. Wenn am
Ende einer Funktion eine Referenz "stirbt", wird nichts zerstoert.

Wahrscheinlich ist auch sinnvoll den Parameter von AddNode mit einem
Parameter vom Typ (const Path &) zu deklarieren. Dein Programm
funktioniert dann trotzdem richtig, denn man kann auch dann

AddNode(Path(0));

sagen. Es wird on the fly ein Objekt Path(0) erzeugt und ein solches
"Luftobjekt" kann an eine Referenz auf ein const-Objekt uebergeben
werden. Wenn Du Objekte vom Typ Path nie *bewusst* kopieren moechtest,
brauchst Du auch keinen Kopierkonstruktor zu definieren. Das
"unbewusste" Kopieren findet nicht mehr statt, wenn Du Path-Objekte
immer nur per Referenz weitergibst (es sei denn, eine weitere Klasse,
die kopiert werden soll, enthaelt Path-Objekte oder Zeiger auf
Path-Objekte).

Du kannst auch erreichen, dass die Klasse Path nicht einmal
versehentlich kopiert werden kann, indem Du sie so

class Path
{
public:
int* way;
int n;
Path(int num);
~Path();
private:
Path(const Path&);
};

definierst und Path(const Path&) gar nicht implementierst. Dann meldet
Dir der Compiler bei einer versehentlichen Wertuebergabe eines
Path-Objekts (wie im Aufruf GetNodeFromPath(p) bei Deiner Version)
einen Fehler, weil der Kopierkonstruktor von Path zwar existiert, aber
wegen der private-Deklaration nicht zugaenglich ist und daher nicht
verwendet werden kann.

--
Horst

--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de

Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ (German) All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.