Seite 1 von 1
[C++]Gegenseitiges includieren
Verfasst: 04.07.2011, 19:29
von Brainsmith
Hallo allerseits, ich hab da eine hoffentlich leicht zu beantwortende Frage.
Es geht um C++.
Ich habe eine Klasse A, die hat einen pointer auf Klasse B (forward deklariert)
Jetzt bekomme ich aber Probleme, weil Klasse B eigentlich auch einen Pointer von Klasse A bräuchte. Ich könnte nun der Klasse B einen Pointer auf Klasse A geben (auch forward deklariert).
Ein Bekannter hat mir allerdings freundlicherweise geflüstert, dass soetwas schlechtes Design ist. Die Frage ist nun:
Was spricht eigentlich dagegen? Ich sehe bei mir nämlich (ohne viel umzuwursteln) keine Möglichkeit das zu umgehen.
Liebe Grüße
Brainsmith
Re: [C++]Gegenseitiges includieren
Verfasst: 04.07.2011, 19:32
von Krishty
Nichts – ist nicht schlechtes Design bei dir sondern eine C++ inhärente Schwäche.
Gruß, Ky
Re: Gegenseitiges includieren
Verfasst: 04.07.2011, 19:32
von dot
Naja, aus der Tasache dass die beiden Klassen sich gegenseitig referenzieren folgt dass sie voneinander abhängig sind. Derartige bidirektionale Assoziationen will man evtl. eher vermeiden. Um die Frage was denn dagegen spricht richtig beantworten zu können müsste man jetzt aber wissen worum es sich bei A und B genau handelt und warum die sich gegenseitig kennen müssen.
Krishty hat geschrieben:Nichts – ist nicht schlechtes Design bei dir sondern eine C++ inhärente Schwäche.
Naja ich seh da jetzt keine wirkliche Schwäche, das ist eben einfach wie C++ funktioniert!? Ein Name ist erst bekannt wenn er deklariert wurde...
Re: [C++]Gegenseitiges includieren
Verfasst: 04.07.2011, 19:45
von Brainsmith
Ich habe die Klasse Sound2D, auf die der Benutzer meiner Soundbibliothek zugreifen wird. Der User instanziiert die Klasse Sound2D beliebig oft und kümmert sich selbst um das Löschen.
Die Klasse beinhaltet Informationen über den abzuspielenden Sound, wie zB den state (IsPlaying, IsPaused, IsStopped), sowie die Methoden Play, Pause, Stop. Die SourceVoices von Xaudio2 sind nun aber in der Klasse SoundManager, weshalb ich Zugriff auf eben diesen brauche.
Sounds abspielen klappt wunderbar, allerdings weiß immer nur der Soundmanager, dass ein Sound zu Ende ist und der Soundmanager hat keinen Pointer vom Sound, weshalb er es ihm nicht sagen kann.
Wenn eine Instanz aus Sound2D nun abspielen will, obwohl schon der state IsPlaying gesetzt ist, mache ich nichts, weil der Sound ja spielt. Da ich dementsprechend keinen Zugriff darauf habe, ob die Voice zu Ende gespielt hat, kann ich jeden Sound2D nur einmal abspielen.
Daher meine Frage.
Ps: Ich hab das Gefühl, mein Programm sitzt mit nem Trollface vor mir....
Re: [C++]Gegenseitiges includieren
Verfasst: 04.07.2011, 19:55
von kaiserludi
jetzt stell dir vor, Klasse 1 ist ein einer externen Library und Klasse 2 in einer App. Spätestens in dem Moment funktioniert das Prinzip nicht mehr, dass beide Klassen Pointer auf die jeweils andere Klasse halten, weil die Lib gar nicht weiß, welche Klassen die App definieren wird.
Man nutzt für solche wechselseitigen Abhängigkeiten normalweise ein Callbackinterface: Klasse 1 deklariert bestimmte Methoden, die Klasse 2 dann implementieren muss. jede Klasse, die diese Methoden implementiert, kann dann Klasse 1 benutzen und ihre entsprechenden Callbackimplementationen können von Klasse 1 aufgerufen werden, ohne dass Klasse 1 über die Existenz der anderen Klasse zur eigenen Kompilierzeit hat Bescheid wissen müssen.
Re: [C++]Gegenseitiges includieren
Verfasst: 04.07.2011, 20:09
von Brainsmith
Hm.. Mist... Ich hab das Callbacksystem noch nicht so ganz verstanden.
Ich definiere also eine Funktion "void SoundCallback();", das mein Soundmanager kennt und die Funktion deklariert ohne, dass ich sie irgendwo implementiere.
Jetzt kommt dementsprechend eine Instanz von Sound2D und implementiert "void SoundCallback();" ?
Ich kann mir das irgendwie nur schwer vorstellen.
Kann mir evtl jemand dafür ein Beispiel mit (Pseudo-)Code geben?
Re: Gegenseitiges includieren
Verfasst: 04.07.2011, 20:18
von Krishty
dot hat geschrieben:Naja ich seh da jetzt keine wirkliche Schwäche, das ist eben einfach wie C++ funktioniert!? Ein Name ist erst bekannt wenn er deklariert wurde...
Der Knackpunkt ist, dass der Name erst verwendet werden darf, wenn er
vorher deklariert wurde, und das ist scheiße und geht deutlich einfacher (siehe Dependent Names in Templates – aber davon, alles zu Templates zu machen, hat man ja auch nicht viel); wenn man das zuendespinnt kann man Header auch komplett aufgeben und hat ein schönes zeitgemäßes Modulsystem wo man nicht mehr alles doppelt formulieren muss. Was entsprechendes war auch für C++0x geplant, aber 14 Jahre reichen ja für die tatsächlich bahnbrechenden Features nicht … :)
Brainsmith hat geschrieben:Die SourceVoices von Xaudio2 sind nun aber in der Klasse SoundManager, weshalb ich Zugriff auf eben diesen brauche.
Bei mir liegen die Source Voices in den Tönen, nicht beim Manager – dann brauchst du den Manager nur noch bei der Erzeugung als Parameter übergeben. Spezielle Gründe dagegen?
kaiserludi hat geschrieben:jetzt stell dir vor, Klasse 1 ist ein einer externen Library und Klasse 2 in einer App. Spätestens in dem Moment funktioniert das Prinzip nicht mehr, dass beide Klassen Pointer auf die jeweils andere Klasse halten, weil die Lib gar nicht weiß, welche Klassen die App definieren wird.
Es ist eine Soundbibliothek; da wäre es ja ein bisschen sinnlos, wenn man Soundmanajah und Sounds trennen würde.
Re: Gegenseitiges includieren
Verfasst: 04.07.2011, 22:06
von dot
Krishty hat geschrieben:dot hat geschrieben:Naja ich seh da jetzt keine wirkliche Schwäche, das ist eben einfach wie C++ funktioniert!? Ein Name ist erst bekannt wenn er deklariert wurde...
Der Knackpunkt ist, dass der Name erst verwendet werden darf, wenn er
vorher deklariert wurde, und das ist scheiße und geht deutlich einfacher [...]
Das stimmt natürlich. Aber wenn du mich fragst ist das auch kein wirkliches Problem. Man muss eben wissen was man tut und das trifft in C++ eh auf alles zu...
Natürlich wäre ein Modulsystem nice to have, aber nicht unbedingt notwendig. Und ich möcht nicht wissen was für Verrenkungen da nötig werden könnten damit das Abwärtskompatibel bleibt.
Krishty hat geschrieben:Brainsmith hat geschrieben:Die SourceVoices von Xaudio2 sind nun aber in der Klasse SoundManager, weshalb ich Zugriff auf eben diesen brauche.
Bei mir liegen die Source Voices in den Tönen, nicht beim Manager – dann brauchst du den Manager nur noch bei der Erzeugung als Parameter übergeben.
Ja, genau das würd ich auch vorschlagen...
Re: [C++]Gegenseitiges includieren
Verfasst: 05.07.2011, 11:46
von Brainsmith
Krishty hat geschrieben:Bei mir liegen die Source Voices in den Tönen, nicht beim Manager – dann brauchst du den Manager nur noch bei der Erzeugung als Parameter übergeben. Spezielle Gründe dagegen?
Hm. Eigentlich wollte ich die Anzahl der parallel abgespielten Voices limitieren, um einen konstanten Ressourcenverbrauch zu gewährleisten.
So müsste ich maximal n Source Voices initialisieren. Falls zu viele Sounds abgespielt würden, könnte ich einfach eine Voice mit weniger Priorität abbrechen. Die Idee an sich ist ja nicht unnötig, oder?
Ich hab noch nicht darüber nachgedacht, ob sich das auch realisieren lässt, wenn die Voices bei den Sounds liegen. Zumindest hätte ich mehr Voices als geplant. Übersehe ich etwas?
Dann bekäme ich wieder ein Problem, denn die Source Voices wissen dementsprechend nichts voneinander und ich könnte nur über Umwege Sounds abbrechen.
Dabei hab ich noch folgende Gedanken: Es gibt (wenn ich mich jetzt nicht vertue) eine Anzahl von maximal parallel abgespielten Voices auf Hardware-Ebene. Allerdings meine ich gelesen zu haben, dass Xaudio2 alles via Software mixt und keine Hardwareunterstützung hat. Dementsprechend müsste immer nur ein einziger voll abgemixter Sound bei der Hardware ankommen, oder?
Re: [C++]Gegenseitiges includieren
Verfasst: 12.08.2011, 18:47
von kaiserludi
Brainsmith hat geschrieben:Hm.. Mist... Ich hab das Callbacksystem noch nicht so ganz verstanden.
Ich definiere also eine Funktion "void SoundCallback();", das mein Soundmanager kennt und die Funktion deklariert ohne, dass ich sie irgendwo implementiere.
Jetzt kommt dementsprechend eine Instanz von Sound2D und implementiert "void SoundCallback();" ?
Ich kann mir das irgendwie nur schwer vorstellen.
Kann mir evtl jemand dafür ein Beispiel mit (Pseudo-)Code geben?
Als Beispiel mal das Debug-Interface einer statischen Library
Code: Alles auswählen
class Base
{
public:
static void setListener(const BaseListener* const baseListener);
protected:
static void debugReturn(const JString& string);
static BaseListener* m_baseListener;
};
Code: Alles auswählen
class BaseListener
{
public:
virtual void debugReturn(const JString& string) = 0;
};
Code: Alles auswählen
BaseListener* Base::m_baseListener = 0;
void Base::setListener(const BaseListener* const baseListener)
{
m_baseListener = const_cast<BaseListener*>(baseListener);
}
void Base::debugReturn(const JString& string)
{
if(m_baseListener)
m_baseListener->debugReturn(string);
}
Diverse Klassen in einer Library produzieren debugOut. Den wollen sie aber nicht einfach in eine Datei oder die Konsole schreiben, denn nur die App weiß, wo sie den Output haben will.
Also deklariert die Lib ein Callbackinterface namens BaseListener mit der Methode debugReturn(). Dazu implementiert sie die Klasse Base, welche einen Pointer auf ein statische Instanz vom BaseListener hält. Wenn eine Klasse der Library nun in ihren Methoden debugOut ausgeben möchte, dann erbst sie von Base und ruft einfach ihre geerbte Methode debugReturn() auf. Diese wiederum ruft nun, falls setListener() aufgerufen wurde und damit der Pointer zum Listener nicht NULL ist, auf diesem dessen debugReturn() auf.
Interessiert es den Programmierer der App nicht, was die Lib an debugout hat, so ruft er einfach nicht setListener() auf. Wenn es ihn hingegen interessiert, dann ruft er diese Methode auf Base auf und übergibt einen Pointer auf eine Klasse der App, welche von BaseListener erbt und dessen debugReturn() Methode implementiert.
Die App kann nun in ihrer Implementation des DebugReturn() selbst festlegen, wo sie den debugOutput der Lib hinschreibt.
Die Lib ruft dazu also eine Methode der App auf, obwohl sie, als sie selbst kompiliert wurde (im Falle statischer Libs) noch gar nicht wusste, von welcher App sie genutzt werden würde, denn sie legt selber das Interface fest, das eine kompatible App unterstützen muss.
Re: [C++]Gegenseitiges includieren
Verfasst: 13.08.2011, 09:36
von sfxon
Wie der letzte Beitrag es in etwa schon beschreibt:
Mit virtuellen Klassen und Vererbung lässt sich soetwas realisieren. Und es spricht eigentlich auch nichts dagegen, wenn du dokumentierst was du dort machst.
Das bedeutet man nutzt eine virtuelle Klasse -welche die oberste Hierarchie bildet.
In dieser werden die benötigten Funktionen deklariert.
Nur diese deklarierten Funktionen lassen sich letztendlich von den beiden Implementierungen der Klasse aus aufrufen.
(Das folgende ist nur Pseudo-Code..)
Code: Alles auswählen
virtual class Mother {
virtual function foo();
virtual function getOtherClass(void *pointer);
void *PointerToOtherClass;
}
Und zwei implementierungen dieser Klasse:
Code: Alles auswählen
class A : Mother {
function foo() { /*do something */
function getOtherClass(void *pointer) { /* save pointer */ }
}
class B : Mother {
function foo();
function getOtherClass(void *pointer) { /* save pointer */ }
}
Wenn man die Klassen jetzt instantiiert hat, kann man anschliessend mit der Funktion getOtherClass einen Zeiger auf die jeweils andere Klasse übergeben.
Bspw.:
Code: Alles auswählen
A *a = new A();
B *b = new B();
a->getOtherClass((Mother*) b);
b->getOtherClass((Mother*) a);
Ist schon länger her das ich mit c++ gearbeitet habe. C++ ist in den letzten Jahren viel flexibler geworden.
Früher hab ich das aber, wenn ich mich richtig erinnere, mit void* Zeigern gemacht.
Re: [C++]Gegenseitiges includieren
Verfasst: 13.08.2011, 12:46
von kimmi
Das Grundproblem nennt man übrigens zyklische Abhängigkeit. Und auch ich rate zum Einsatz von Listenern, da diese gern Leaks produzieren.
Gruß Kimmi