 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Marco Guest
|
Posted: Mon Sep 13, 2004 6:55 pm Post subject: Design Frage |
|
|
Hi Group,
ich habe eine Design Frage. Ich habe folgende Klassen:
class JointGroup
{
public:
JointGroup();
virtual ~JointGroup();
//Joints zur JointGroup hinzufügen
JointA* addJointA();
JointB* addJointB();
JointC* addJointC();
//Einen Joint entfernen
void removeJoint(Joint* pJoint);
//Alle Joints entfernen
void removeAllJoints();
private:
std::map<Joint*, Joint*> m_jointMap;
}
class Joint
{
friend class JointGroup;
protected:
Joint();
virtual ~Joint();
public:
Einige public methoden
}
JointA, JointB, JointC sind von Joint abgeleitet.
Was ich damit tun möchte ist folgendes:
Die JointGroup soll ein Container für alle Joints sein. Nur die
JointGroup kann Joints unterschiedlichen Typs erzeugen. (Ein Joint
ohne JointGroup würde keinen Sinn ergeben)
Die JointGroup speichert und verwaltet die Joints in der Map.
Über die addJoint Methoden werden Instanzen der entsprechenden Joints
mit new angelegt. Über die zurückgebenen Zeiger können die Joint
Methoden aufgerufen werden. Wenn ich einen Joint löschen möchte, rufe
ich JointGroup::removeJoint(ptr) auf. Intern wird der Joint mit delete
gelöscht. Soweit so gut.
Nun könnte man aber auf die Idee kommen, die JointGroup zu löschen und
nach dem Löschen auf die Joint Zeiger zuzugreifen. Diese sind aber
ungültig, da sie ja bereits von der JointGroup gelöscht wurden. Hier
kommt sicher das Stichwort Smart Pointer ins Spiel. Allerdings
verlieren die Joints ohne entsprechende JointGroup ihren Sinn und Ihre
Funktion. Mir schwebt nun gerade vor, beim Destriuktor Aufruf der
JointGroup die Joints nicht zu löschen, sondern ein "Invaild" Flag in
jedem Joint zu setzen. Beim Aufruf einer Joint Methode könnte man das
Flag abfragen und gegebenenfalls eine Exception werfen.
Irgendwie bin ich aber noch nicht so glücklich mit dem Konzept.
Vielleicht hat irgendjemand eine bessere oder völlig andere Idee ?
Gruss
Marco
--
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 |
|
 |
Boris Glawe Guest
|
Posted: Mon Sep 13, 2004 8:45 pm Post subject: Re: Design Frage |
|
|
Marco wrote:
| Quote: | Hi Group,
ich habe eine Design Frage. Ich habe folgende Klassen:
class JointGroup
{
public:
JointGroup();
virtual ~JointGroup();
//Joints zur JointGroup hinzufügen
JointA* addJointA();
JointB* addJointB();
JointC* addJointC();
|
/*
Das hier macht so keinen Sinn. Ich würde die Methode eher in createJointX
umbennen. createXXX ist häufig die Konvention, wenn der zurückgegebene Pointer
auf einen eben dynamisch erzeugten Speicher zeigt. Dieser Speicher lebt dann
weiter, auch wenn deine JointGroup gelöscht wird - eben weil er dynamisch ist.
Wenn dein Destruktor nicht explizit diese Speicherbereiche, die die addJointX()
Methoden erzeugt haben, wieder löscht, dann hast du auch nie ungültige Pointer.
In der Regel ist der Aufrufer solcher Mehoden dafür zuständig, den Speicher
wieder freizugeben.
Es ist daher nicht unbedingt sinnvoll, dass diese Joints in der Klasse
gespeichert werden (kommt aber darauf an, was deine Klasse vor hat). Es reicht
häufig, einfach Methoden bereitzustellen, die den Speicher reservieren,
initialisieren und einen Pointer auf den Speicher deiner Joints zurückgeben. Ab
dann hat die Klasse nichts mehr mit dem Memorymanagement zu tun.
Möchtest du aber Joint-Objekte in einem Container speichern, dann speichere
nicht die Objekte selbst, sondern die Pointer auf deren Speicherbereich, was an
vielen Stellen zudem noch ordentlich Performanz bringt.
Ähnliches gilt, wenn du eine Klasse baust, die deinen Speicher verwalten soll
(was du hier vermutlich vor hast). Diese Klasse speichert einfach nur die
Pointer auf die Objekte. Da diese Klasse schließlich die Aufgabe hat, den
Speicher zu verwalten, kann man ziemlich sicher von einem Designfehler sprechen,
wenn diese Klasse zur Laufzeit gelöscht werden würde. Solche Klassen werden also
ganz am Ende wieder gelöscht, wenn die enthaltenen Pointer nicht mehr gebraucht
werden. Mit dem Löschen der Klasse würden dann auch alle enthaltenen Joints
gelöscht werden.
*/
| Quote: |
//Einen Joint entfernen
void removeJoint(Joint* pJoint);
//Alle Joints entfernen
void removeAllJoints();
private:
std::map<Joint*, Joint*> m_jointMap;
}
class Joint
{
friend class JointGroup;
protected:
Joint();
virtual ~Joint();
public:
Einige public methoden
}
JointA, JointB, JointC sind von Joint abgeleitet.
Was ich damit tun möchte ist folgendes:
Die JointGroup soll ein Container für alle Joints sein. Nur die
JointGroup kann Joints unterschiedlichen Typs erzeugen. (Ein Joint
ohne JointGroup würde keinen Sinn ergeben)
Die JointGroup speichert und verwaltet die Joints in der Map.
Über die addJoint Methoden werden Instanzen der entsprechenden Joints
mit new angelegt. Über die zurückgebenen Zeiger können die Joint
Methoden aufgerufen werden. Wenn ich einen Joint löschen möchte, rufe
ich JointGroup::removeJoint(ptr) auf. Intern wird der Joint mit delete
gelöscht. Soweit so gut.
Nun könnte man aber auf die Idee kommen, die JointGroup zu löschen und
nach dem Löschen auf die Joint Zeiger zuzugreifen. Diese sind aber
ungültig, da sie ja bereits von der JointGroup gelöscht wurden. Hier
kommt sicher das Stichwort Smart Pointer ins Spiel. Allerdings
verlieren die Joints ohne entsprechende JointGroup ihren Sinn und Ihre
Funktion. Mir schwebt nun gerade vor, beim Destriuktor Aufruf der
JointGroup die Joints nicht zu löschen, sondern ein "Invaild" Flag in
jedem Joint zu setzen. Beim Aufruf einer Joint Methode könnte man das
Flag abfragen und gegebenenfalls eine Exception werfen.
Irgendwie bin ich aber noch nicht so glücklich mit dem Konzept.
Vielleicht hat irgendjemand eine bessere oder völlig andere Idee ?
|
Trenne die beiden Konzepte "Erzeugen der Objekte" und "Speichern der Objekte".
Die eine Klasse erzeugt dir die Objekte (häufig wird so etwas von static
Methoden übernommen, weil die Erzeugung in der Regel nicht auf Eigenschaften
eines bestimmten Objektes angewiesen ist), die andere speichert z.B eine Liste
von Pointern auf die Objekte. Was sich damit allerdings nicht erreicht lässt,
ist dass du dich nach wie vor um die Speicherverwaltung kümmern musst. Wenn du
einen Joint nicht mehr brauchst, dann musst du ihn auch explizit löschen. Und da
du das sowieso machen musst, brauchst du eigenlicht auch keine Klasse, die die
das Memorymanagement abnimmt. Solltest du nicht ausgebuffte Algorithmen für so
etwas implementieren, dann ist das Konzept des Speichern und Wiederfinden von
Objekten mit Hilfe von maps sowieso die größte Performancebremse. Ich schätze
mal dein Programm Faktor 10 schneller läuft, wenn du dich selbst um den Speicher
kümmerst aber dafür direkte Zugriff auf den Pointer hast. Wenn man sich
Container für Objekte bastelt, dann macht man das in der Regel nicht, um deren
Speicher zu verwalten, sondern weil man eben eine Mengenoperation durchführen
möchte.
Häufig hat man auch den Fall, dass ein Objekt aus Mehreren zusammengesetzt ist.
Dann macht es aber in der Regel auch keinen Sinn, die "Innereien" mit dem
Löschen den Objekts noch weiter im Speicher zu lassen. Daher kann man die
inneren Objekte auch vom Destruktor wieder freigeben lassen. Sollten dann noch
Pointer auf die eben gelöschten inneren Objekte existieren, dann liegt bestimmt
ein Designfehler vor, weil man dann das umschließende Objekt noch nicht hätte
löschen dürfen. Hier mit smartpointern zu arbeiten wäre - da kann ich auch
falsch liegen - eine Zweckentfremdung.
Was man auch machen kann, ist das neue Objekte intern zu speichern und eine
Referenz auf das Objekt zurückgeben. Objekte leben immer solange, bis die letzte
Referenz gelöscht wird. Allerdings sind Refenzen etwas unhandlicher bei
Mengenoperationen, weil man z.B. von ihnen keinen Arrays erstellen kann.
Referenzen werde üblicherweise benutzt, um Performant und sicher
Funktionsparameter zu übergeben.
Grüße boris
--
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 |
|
 |
Steffen Rauh Guest
|
Posted: Mon Sep 13, 2004 9:20 pm Post subject: Re: Design Frage |
|
|
Hi Marco. Im Usenet wird üblicherweise der volle Real-Name verwendet.
Ich hoffe, du willst das nicht rauchen. ;-)
| Quote: | Die JointGroup soll ein Container für alle Joints sein. Nur die
JointGroup kann Joints unterschiedlichen Typs erzeugen. (Ein Joint
ohne JointGroup würde keinen Sinn ergeben)
|
Macht es Sinn, dass addJointX immer einen JointX zurück gibt und keinen
Joint? Wenn JointGroup nur Joint verwalten soll und ihm egal ist, ob es sich
um einen JointX handelt, sollte die Erzeugung dieser Objekte aus der Klasse
an die Stelle ausgelagert werden, an der es wichtig ist, ob es sich um einen
JointX handelt. Eventuell wäre ein Prototype Design Pattern angebracht.
| Quote: | Nun könnte man aber auf die Idee kommen, die JointGroup zu löschen und
nach dem Löschen auf die Joint Zeiger zuzugreifen. Diese sind aber
ungültig, da sie ja bereits von der JointGroup gelöscht wurden. Hier
kommt sicher das Stichwort Smart Pointer ins Spiel. Allerdings
verlieren die Joints ohne entsprechende JointGroup ihren Sinn und Ihre
Funktion. Mir schwebt nun gerade vor, beim Destriuktor Aufruf der
JointGroup die Joints nicht zu löschen, sondern ein "Invaild" Flag in
jedem Joint zu setzen. Beim Aufruf einer Joint Methode könnte man das
Flag abfragen und gegebenenfalls eine Exception werfen.
|
Und wer gibt dann deine Joint-Objekte frei, wenn es JointGroup nicht mehr
tut?
| Quote: | Irgendwie bin ich aber noch nicht so glücklich mit dem Konzept.
Vielleicht hat irgendjemand eine bessere oder völlig andere Idee ?
|
Ich finde es seltsam, dass du designtechnisch JointGroup-Objekte löschen
lässt, wenn es sein kann, dass noch Joint-Pointer unterwegs sind. Aber wird
wohl schon irgendwie seinen Grund haben (hoffe ich mal).
Denkbar wäre noch, dass der Code, welcher die Joint-Pointer verwendet, einen
Callback bei JointGroup registriert für den Fall, dass das Objekt zerstört
werden sollte. Der Callback stellt dann sicher, dass alle Joint-Pointer
entschärft werden und kehrt erst danach zu JointGroup zurück. Dadurch wartet
JointGroup auf gewisse Weise mit seiner Zerstörung auf seine Joint-Objekte.
Gehe ich recht in der Annahme, dass deine Anwendung multithreaded ist?
Zumindest kann ich mir gerade nur dort Situationen vorstellen, wo dein
Problem ein wirklich Ernsthaftes sein könnte.
MfG,
Steffen Rauh
--
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 |
|
 |
Christoph Basedau Guest
|
Posted: Tue Sep 14, 2004 12:31 am Post subject: Re: Design Frage |
|
|
13.09.2004 20:55, Marco schrieb:
| Quote: | ich habe eine Design Frage. Ich habe folgende Klassen:
class JointGroup
{
public:
JointGroup();
virtual ~JointGroup();
//Joints zur JointGroup hinzufügen
[verwaltet verschiedene Joint-Klassen] |
| Quote: |
JointA, JointB, JointC sind von Joint abgeleitet.
Was ich damit tun möchte ist folgendes:
Die JointGroup soll ein Container für alle Joints sein. Nur die
JointGroup kann Joints unterschiedlichen Typs erzeugen. (Ein Joint
ohne JointGroup würde keinen Sinn ergeben)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| Quote: | Die JointGroup speichert und verwaltet die Joints in der Map.
Nun könnte man aber auf die Idee kommen, die JointGroup zu löschen und
nach dem Löschen auf die Joint Zeiger zuzugreifen. Diese sind aber
ungültig, da sie ja bereits von der JointGroup gelöscht wurden. Allerdings
verlieren die Joints ohne entsprechende JointGroup ihren Sinn und Ihre
Funktion. Mir schwebt nun gerade vor, beim Destriuktor Aufruf der
JointGroup die Joints nicht zu löschen, sondern ein "Invaild" Flag in
jedem Joint zu setzen. Beim Aufruf einer Joint Methode könnte man das
Flag abfragen und gegebenenfalls eine Exception werfen.
Irgendwie bin ich aber noch nicht so glücklich mit dem Konzept.
Vielleicht hat irgendjemand eine bessere oder völlig andere Idee ?
|
Wenn obige Aussage stimmt, dass Joints ohne übergeordnete JointGroup keinen
Sinn machen, dann solltest Du Joints ohne Group eben nicht erhalten.
Wenn es dagegen denkbar ist, dass Joints (was immer ein Joint in diesem
Zusammenhang ist vorübergehend group-los sein können und nachträglich
z.B. einer anderen Group-Instanz zugeordnet werden - oder auch, um nach
Löschen einer Group auf die Daten eines grouplos-gewordenen Joints
zugreifen zu können, dann hätte Plan B eine Sinn (das mit dem invalid-flag).
Kurz: Du solltest Dir einfach zunächst überlegen, _was_ Du abbilden
willst. Welche Abhängigkeiten und konditionalen Beziehungen zwischen Groups
und Joints bestehen (OOA, dann OOD. OOP am Ende).
Wenn ein Joint nicht zwingend unter allen denkbaren Umständen der ursprünglichen
Group angehört, dann sollte man ihn auch nicht zwingend mit Dekonstruktion
der Gruppe dekonstruieren.
Eventuell könntest Du ja eine weitere Klasse für gebrauchte Joints entwerfen
(vielleicht SmokedA, SmokedB, SmokedC ?), in die Du die Daten eines
Joints hineinkopierst, die nach Hinscheiden seiner Group noch gebraucht
werden können. Wenn das bei Dekonstruktion der ursprünglichen Grupp absehbar
ist, kann das ja im Dekonstruktor der Gruppe erfolgen.
Die 'Ehemaligen' könntest Du ja in einer weiteren Group-Klasse verwalten,
um den Überblick über die ehemaligen Joints zu wahren.
(Veteranen- bzw Friedhofsverwaltung sozusagen).
--
Gruesse, Christoph
Rio Riay Riayo - Gordon Sumner, 1979
--
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 |
|
 |
Martin Kaul Guest
|
Posted: Tue Sep 14, 2004 7:11 am Post subject: Re: Design Frage |
|
|
Marco wrote:
| Quote: | Nun könnte man aber auf die Idee kommen, die JointGroup zu löschen und
nach dem Löschen auf die Joint Zeiger zuzugreifen. Diese sind aber
ungültig, da sie ja bereits von der JointGroup gelöscht wurden. Hier
kommt sicher das Stichwort Smart Pointer ins Spiel. Allerdings
verlieren die Joints ohne entsprechende JointGroup ihren Sinn und Ihre
Funktion. Mir schwebt nun gerade vor, beim Destriuktor Aufruf der
JointGroup die Joints nicht zu löschen, sondern ein "Invaild" Flag in
jedem Joint zu setzen. Beim Aufruf einer Joint Methode könnte man das
Flag abfragen und gegebenenfalls eine Exception werfen.
Irgendwie bin ich aber noch nicht so glücklich mit dem Konzept.
hmm, sieht IMHO aus zwei Gründen nicht so toll aus: |
1.) Eigentlich sollte es ja nie passieren, das JointGroup gelöscht
wird und noch Joints rumschwirren, d.h. dein Vergleich des
Invalid-Flag ist also in den allermeisten Fällen unnötig und
doch in jedem Aufruf (d.h. immer bei Verwendung der
Joint Instanzen) dabei.
2.) Exceptions gefallen mir persönlich in der Zwischenzeit nicht
mehr (als bewusste Anwendung) um einen Fehler (oder ähnliches)
abzufangen, da ich mir immer um den passenden catch Gedanken
machen muss - bei Aufgaben, bei denen ich weiss, dass ein
Fehler/Problem auftreten kann, versuche ich den Fehler gleich
vor dem Entstehen abzufangen.
| Quote: | Vielleicht hat irgendjemand eine bessere oder völlig andere Idee ?
In ähnlichen Fällen habe ich das Observer-Pattern verwendet, wobei |
Joint den Subject-Part des Pattern darstellt. Ein notify wird dann
halt nur vor dem Löschen des Joint ausgeführt.
Die Teile, die einen Joint benutzen, müssen sich halt als Observer
auf Joint registrieren und bekommen dann ne Mitteilung (notify), dass
die Joint Instanz gelöscht wird.
tschaule
Martin
--
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 |
|
 |
|
|
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
|
|