selbes Objekt in mehreren Klassen nutzen
-
Singletons sind generell böse, sollte man sich gleich garnicht angewöhnen.
Erzeug das Objekt in deiner main() Funktion (bzw. InitInstance oder was auch immer), und gib es per Pointer an alle Klassen die es benötigen (z.B. als Parameter des Konstruktor, oder als Parameter bei allen Funktionen die es benötigen).
-
wieso sind singeltons böse?
die umsetzung war jetzt relativ leicht.
allerdings funktionierts auch noch nich richtig ^^gibts keine einfachere Methode, als jeder funktion das Objekt zu übergeben?
sind inzwischen n ganzen haufen funktionen.greetz
-
sW00p schrieb:
wieso sind singeltons böse?
die umsetzung war jetzt relativ leicht.
allerdings funktionierts auch noch nich richtig ^^die umsetzung ist eben nicht ganz so einfach. es gibt viel zu beachten, viel falsch zu machen.
schau mal hier, da wird auch kurz auf singletons eingegangen:
http://www.c-plusplus.net/forum/viewtopic-var-t-is-155350.htmlgibts keine einfachere Methode, als jeder funktion das Objekt zu übergeben?
sind inzwischen n ganzen haufen funktionen.zeiger zu übergeben ist das gebräuchlichste. Wir wissen nicht, wozu du das brauchst, aber solange es nicht zwingend(!) ist, dass das Objekt einzigartig ist, ist das singleton gefährlich. Eben weil sich mal was ändert, und schwupps hast du mehrere objekte von dem typ.
Ansonsten kannst du auch objektzeiger innerhalb der Klassen speichern, dann brauchst du den nicht an jede memberfunktion übergeben(übergibs einfach im constructor, vielleicht bietet sich bei dir auch ein factory pattern an, dann wird da halt automatisch der zeiger übergeben. aber für mehr tipps fehlen halt informationen ;)).
-
ich habe halt die klasse CSettings, um darin einstellungen zu speichern, die in dem Programm (Gui) gemacht werden.und dann eben noch andere klassen in dem programm.
aber von allen klassen müssen auch die einstellungen ausgelesen und gespeichert werden. sprich von überall muss der zugriff auf dasselbe objekt gewährleistet sein.
greetz
-
also ich abe jetzt mal probiert, das ganze wie pointerübergabe umzuarbeiten, habe nun aber folgendes problem:
ich rufe mal einen menüpunkt auf bei dem sich ein neues dialogfeld öffnet.
mit der DoModal() funktion
da kann nich ja jetzt nicht einfach ein objekt übergeben.
ist es möglich vielleicht in der haupt klasse ein objekt "CSettings settings;" zu erzeugen und in in allen anderen klassen jeweils ein objekt der haupt-klasse über dass sie dann auf das objekt der CSettings klasse zugreifen können?
oder würde dabei dann nicht ein und dasselbe objekt benutzt werden?
ich denke da an sowas wie:objektinklasse->CSettingsobjekt->zugriffaufdatenvonCSettings
/EDIT/
habs grade mal getestet, also der zugriff funktioniert schonmal.
jetzt nur zurück zu der frage:
wird da imemr dasselbe objekt der CSettings klasse verwendet?
da wär halt schon essentiell wichtig ^^
-
Hallo,
also sofern Du Probleme hast das "einmal-erzeugte" Objekt an alle weiteren Bedürftigen weiterzugeben, dann hast Du schon mal ein grundlegenden Designfehler.
Hier noch mal die Vorgehensweise:
1.) Erzeuge das Objekt auf obersten (gebrauchten) Ebene. Dh. im schlimmsten Fall in der main().
2.) Da alle anderen Objekte direkt oder indirekt in der obersten Ebene instanziert wurden (das kannst Du Dir als Baumstruktur vorstellen), ist es doch das Intuitivste diesen (Unter-)Objekten eine Referenz auf das gefragte Objekt zu übergeben (zB jeweils im Konstruktor) und dann u.U. weiterzugeben.Diese Vorgehensweise ist zwar die intuitivste, macht aber nur dann Sinn wenn die "Baumtiefe" nicht alzu hoch ist, (dh die Anzahl der Weitergaben nicht zu hoch ist).
Wenn es Dir zu aufwändig ist für alle Klassen die die CSetting nutzen entsprechende Methoden zu schreiben, dann nutze Mehrfachvererbung:
class NeedSettings { public: void attachSettings(Settings &settings) { this->settings = settings; } private: CSetting &settings; } class A : NeedSettings { // } class B : NeedSettings { // }Bzw Du verzichtest auf ein Settings-Objekt und sagst: "Jedes Objekt das Zugriff auf Settings hat, ist auch ein CSettings objekt". Dazu musst Du CSettings entsprechend anpassen.
Ist es tatsächlich notwendig, dass alle Klassen direkten Zugriff auf dieses Objekt brauchen?
Falls ja, dann ist meines Erachtens das Singleton doch die einfachste Lösung. Was gibt es Einfacheres als sich das Objekt einfach zu holen, da wo es gebraucht wird?Bsp (Klasse A, B und C brauchen das gleiche Objekt D):
class A { void hierWirdsGebraucht() { D &d = D.instance(); } } class B { void hierWirdsGebraucht() { D &d = D.instance(); } } class C { void hierWirdsGebraucht() { D &d = D.instance(); } }Dies ist eine sehr einfache Lösung. Sie ist sehr dynamisch, da sich der Programmierer keine Gedanken über irgendwelche Weitergaben machen muss.
Tip:
Laut Deiner Beschreibung erstellst Du eine GUI. Dh. unter Umständen hast Du mehrere Dialoge, die auf dem Gleichen Objekt arbeiten (im Weiteren Modell genannt). Hierzu gibt es ein pattern MVC (Modell View Controler, auch Observer genannt).1.) Erstelle einmal Das Modell
2.) Erstelle (zentral) alle Dialoge
3.) Registriere allen Dialoge die informiert werden wollen im Modell.
Resultat: alle Dialoge die Du nutzt werden automatisch bei Änderung des Modells upgedatet.Gruß
-
Ein googlen nach DoModal brachte mir, dass du die mfc benutzt.Nun, das schränkt uns schonmal etwas ein. Würde sagen, ab nach mfc, da könnte dir besser geholfen werden, oder? Da kennt man sich sicher besser aus, was funktioniert und was nicht
-
sW00p schrieb:
thx für die schnelle antwort.
und wo muss ich dann das objekt erzeugen?
wieso sollte das ein Problem sein ?struct A { // Gemeinsam zu nutzende Objekt A() : i(1) {} int i; }; class B { // hält eine Referenz auf ein A A& meinA; public: B(A& a) meinA(a) {} void incA() {++a.i; } }; class C { // hält einen Pointer auf ein A A* meinA; public: C(A* a) meinA(a) {} void incA() {++a->i; } }; int main() { A a; B b(a); C c(&a); // ich kann HIER a ändern a.i++; // ... oder auch in b b.incA(); // ... oder auch in c c.incA(); // ... alle drei verweisen auf dasselbe A-Objekt return 0; }Ich weiß gar nicht, wieso da plötlzich singletons und Ähnliches her müssen ...
Gruß,
Simon2.
-
weil singleton zum einen den code kürzer und lesbarer hält und zum anderen für konsistentere beziehungen zwischen den klassen sorgt. ohne singleton schleppt jede klasse nen member mit sich rum, der da semantisch überhaupt nix zu suchen hat.
ausserdem sorgt das singleton pattern dafür, dass garantiert zu jedem zeitpunkt immer nur exakt eine instanz des objekts bestehen kann.
-
Hi,
natürlich hat das singleton-Pattern auch seine Berechtigung. Mir fällt aber auf, dass diese Technik oftmals als Standardlösung ins Spiel gebracht wird - auch dann, wenn Anfänger einfach fragen "Wie kann ich ein Objekt in einem anderen zugreifen ?" (gerne wird auch "Klasse" gesagt) und andere Lösungen naheliegender und passender sind.
Mir scheint's also ob mit dem Aufschwung der singletons und statics langsam aber sicher wieder globale Variablen durch's Hintertürchen eine Renaissance feiern - und bei allen Vorteilen eben auch deren Nachteile. Sehr schön spätestens dann zu sehen, wenn das singleton dann ein Container mit möglchst unterschiedlichen Typen sein soll.
Ob das hier der Fall ist, weiß ich nicht, aber mich wundert, dass die Form von Objektverknüpfung, die die Sprache standardmäßig anbietet, nicht mal mehr erwähnt wird ...
IMO sollte die Herangehensweise andersherum sein: Erst mal die Aggregation, Referenz, Zeiger vorschlagen und erst wenn es wirkliche Gründe dagegen gibt, ein singleton vorschlagen (oder ein Überdenken der Anforderungen).Ja, es hat etwas Gutes, dass eine jede Klasse seine eigene Objektverknüpfung hat, denn die Tatsachen, dass es eine solche braucht, bedeutet noch nicht, dass alle Objekte mit demselben verknüpft werden solle/müssen. Dafür bietet die Referenz-/Pointerlösung eben BEIDES an.
Außerdem verlangt sie vom Designer, sich gründlicher über sein Design Gedanken zu machen ... Gedanken, die jedem Projekt guttun.
Gruß,
Simon2.
-
Simon2 schrieb:
sW00p schrieb:
thx für die schnelle antwort.
und wo muss ich dann das objekt erzeugen?
wieso sollte das ein Problem sein ?struct A { // Gemeinsam zu nutzende Objekt A() : i(1) {} int i; }; class B { // hält eine Referenz auf ein A A& meinA; public: B(A& a) meinA(a) {} void incA() {++a.i; } }; class C { // hält einen Pointer auf ein A A* meinA; public: C(A* a) meinA(a) {} void incA() {++a->i; } }; int main() { A a; B b(a); C c(&a); // ich kann HIER a ändern a.i++; // ... oder auch in b b.incA(); // ... oder auch in c c.incA(); // ... alle drei verweisen auf dasselbe A-Objekt return 0; }Ich weiß gar nicht, wieso da plötlzich singletons und Ähnliches her müssen ...
Gruß,
Simon2.
ich glaub diese lösung scheint mir bisher am sinnvollsten.
kann ich das ganze auch anstatt mit einer struktur mit einer klasse lösen?
so dass ich die get und set methoden der member variablen direkt in klasse A schreibe?
was ich noch nicht so ganz verstanden habe ist das hier:
A() : i(1) {}@linu(x)bie:
mehrfachvererbung geht desswegen nicht, da die objekte die ich erzeuge, schon abgeleitet sind, nämlich von CDialog.
dem konstruktor von CDialog kann ich kann ich ja auch keine weiteren parameter mitgeben, der au das objekt verweißt oder?
der stanadart konstuktor sieht so aus:
CSettings_general(CWnd* pParent = NULL);generell wäre es ja kein problem den konstruktor auf
CSettings_general(CWnd* pParent = NULL,CSettings* settings);
zu ändern, meine angst ist es halt, wenn ich am standard konstruktor (der von vc++ angelegt wurde)rumpfusche, das ganze nicht mehr richtig funktioniert.
-
sW00p schrieb:
...
kann ich das ganze auch anstatt mit einer struktur mit einer klasse lösen?
...Ja !
ich schreibe hier meistens "struct", weil ich mir damit das Ganze private/public-Gedöns spare.
Merke: "struct" = "class mit Default=public"
Mit struct kannst Du alles machen, was Du mit einer class kannst (Memberfunktionen, Konstruktoren/Destruktoren ("Ctor/DTor"), vererben, sogar private-Bereiche definieren, ...)sW00p schrieb:
...
A() : i(1) {}
...(Ich weiß nicht genau, was Du daran nicht verstehst, deswegen rate ich mal:)
Einfache Initialisierungen ("i(1)") kannst Du in der sog. "Initialisierungsliste" (*) durchführen; Vorteil: Member (hier i) werden nur einmal initialisiert und bisweilen (bei Referenzen) geht es gar nicht anders.(*) gekennzeichnet mit einem ":" VOR dem Funktionsrumpf, also vor dem ersten "{".
sW00p schrieb:
...
ich glaub diese lösung scheint mir bisher am sinnvollsten....:p

Gruß,
Simon2.
-
sW00p schrieb:
...
A() : i(1) {}
...(Ich weiß nicht genau, was Du daran nicht verstehst, deswegen rate ich mal:)
Einfache Initialisierungen ("i(1)") kannst Du in der sog. "Initialisierungsliste" (*) durchführen; Vorteil: Member (hier i) werden nur einmal initialisiert und bisweilen (bei Referenzen) geht es gar nicht anders.(*) gekennzeichnet mit einem ":" VOR dem Funktionsrumpf, also vor dem ersten "{".
d.h. das wäre dasselbe wie:
class A{
public:
int i;
A(){ i=1;};
}????
/Edit/
ich hab die Methode mit dem Pointer jetzt mal ausprobiert und bekomme da nur folgenden fehler:
syntax error : missing ';' before identifier 'settings'mein code ist dieser hier:
class CSsaDlg: public CDialog{
CSettings* settings;
public:
CSsaDlg(CSettings* set) settings(set);
}genauso habe ich mal versucht an dem standard konstruktor rumzupfuschen:
class CSsaDlg : public CDialog
{
// Construction
CSettings* settings;
public:
CSsaDlg(CWnd* pParent = NULL,CSettings* set) settings(set);
}kommt aber genau dasselbe.
greetz
-
sW00p schrieb:
...
d.h. das wäre dasselbe wie:class A{
public:
int i;
A(){ i=1;};
}
[/cpp]Nicht ganz !
A(){ i=1;};wird intern zu
A() : i() { // i wird erstmal "default-konstruiert" .. i=1; // ... und dann zugewiesen };sW00p schrieb:
...
CSsaDlg(CSettings* set) settings(set); ... CSsaDlg(CWnd* pParent = NULL,CSettings* set) settings(set);Du hast auch nicht genau hingesehen beim Abtippen: Die Initialisierungsliste
- wird mit einem Doppelpunkt eingeleitet und
- ist Teil der Implementierung des Konstruktors; Deswegen müssen mindestens leere "{}" folgen und wenn man die Implementierung getrennt wird (z.B. in ein cpp-File ausgelagert), gehört sie auch dahin.
=>
CSsaDlg(CSettings* set) : settings(set) {} ... CSsaDlg(CWnd* pParent = NULL,CSettings* set) : settings(set) {}
BTW1: In C++ ist es üblicher, statt "NULL" direkt eine "0" zu nehmen....
BTW2: Nach Default-Parametern ("pParent = NULL") darf kein "verpflichtender" mehr kommen (woher soll der Compiler sonst die Zuordnung hinbekommen ?). Also: Entweder auch für "set" einen Defaultwert angeben oder Parameter vertauschen oder Deafultwert für pParent weglassen.Gruß,
Simon2.
-
Simon2 schrieb:
sW00p schrieb:
...
d.h. das wäre dasselbe wie:class A{
public:
int i;
A(){ i=1;};
}
[/cpp]Nicht ganz !
A(){ i=1;};wird intern zu
A() : i() { // i wird erstmal "default-konstruiert" .. i=1; // ... und dann zugewiesen };sW00p schrieb:
...
CSsaDlg(CSettings* set) settings(set); ... CSsaDlg(CWnd* pParent = NULL,CSettings* set) settings(set);Du hast auch nicht genau hingesehen beim Abtippen: Die Initialisierungsliste
- wird mit einem Doppelpunkt eingeleitet und
- ist Teil der Implementierung des Konstruktors; Deswegen müssen mindestens leere "{}" folgen und wenn man die Implementierung getrennt wird (z.B. in ein cpp-File ausgelagert), gehört sie auch dahin.
verdammt du ahst recht ^^ weggeschaut, ignoriert, gekniffen *fg
=>
CSsaDlg(CSettings* set) : settings(set) {} ... CSsaDlg(CWnd* pParent = NULL,CSettings* set) : settings(set) {}
BTW1: In C++ ist es üblicher, statt "NULL" direkt eine "0" zu nehmen....
BTW2: Nach Default-Parametern ("pParent = NULL") darf kein "verpflichtender" mehr kommen (woher soll der Compiler sonst die Zuordnung hinbekommen ?). Also: Entweder auch für "set" einen Defaultwert angeben oder Parameter vertauschen oder Deafultwert für pParent weglassen.Gruß,
Simon2.
werde ich mal ausprobieren ob das mit dem vertauschen funzt.
zu btw1:
der konstruktor wurde vom visual c++ erstellt, daher NULL und nich 0, aber danke für den hinweis
zu btw2:
kann ich denn den default wert für pParent einfach weglassen, wenn doch vc in extra erzeugt hat?/EDIT/
oh was mit da grad auffält was ich vergessen ahtte hinzuschreiben ist:
das mitCSsaDlg(CSettings* set) settings(set);
...
CSsaDlg(CWnd* pParent = NULL,CSettings* set) settings(set);bezog sich hierauf:
class C { // hält einen Pointer auf ein A
A* meinA;
public:
C(A* a) meinA(a) {}
void incA() {++a->i; }
};und da ist ja auch kein : zu sehen.
greetz
/EDIT2/
so ich glaub jetzt hab ich kapiert wie du das meinst.geht auch mit dem : ^^
ich glaub wir ham blos ein wenig aneinander vorbeigeredet, ich versuch das hier nochmal zu verdeutlichen. Wir sind aber nahe an der Lösung ^^class CSettings { public: CSettings(); . . private: . . . }; class CSsaDlg : public CDialog { public: CSsaDlg(CSettings* settings,CWnd* pParent = NULL) . . private: . . . protected: . . . }; class CSettings_general : public CDialog { public: CSettings_general(CSettings* set,CWnd* pParent = NULL); . . private: . . . protected: . . . };jetzt brauche ich in den klassen CSsaDlg und CSettings_general dasselbe objekt von CSettings.später kommen noch weitere klassen dazu, die auch wieder genau desselbe objekt von CSettings haben sollen.sprich CSettings ist global einmal vorhanden.
eine main hab ich nicht, da GUI.
ich hoffe nun ist deutlich geworden was ich brauche *gg
manchmal drück ich mich ein wenig unverständlich aus ^^greetz