Forum: PC-Programmierung C++ "deadly diamond of death"


von Rasputin (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich habe ein Diamantenproblem. Leider konnte ich im Netz keine Lösung 
finden. (aber zumindest hab ich herausgefunden, wie sich das Problem 
nennt. ;) )

Ich möchte folgende Klassen:

Vereinfacht aus "diamond_a.cpp"
1
class istream {
2
protected:
3
   application *app;
4
}
5
class ostream {
6
protected:
7
   application *app;
8
}
9
class test : public istream, public ostream {
10
// auch in dieser Klasse wird app benötigt
11
}

hierbei gilt: innerhalb der Vererbung zeigt istream::app, ostream::app, 
test::app immer auf das selbe Objekt (muss!)! Nur der Compiler wird das 
vermutlich nicht verstehen. Darum dachte ich, ich mache eine Basisklasse 
für istream und ostream:

Vereinfacht aus "diamond_b.cpp"
1
class iostream {
2
protected:
3
   application *app;
4
};
5
class istream : virtual public iostream {
6
// in dieser Klasse wird app benötigt
7
8
};
9
class ostream : virtual public iostream {
10
// in dieser Klasse wird app benötigt
11
12
};
13
class test : public istream, public ostream {
14
// in dieser Klasse wird app benötigt
15
};

nur wie baue ich jetzt die constructors für die jeweiligen Klassen, dass 
alles korrekt initialisiert wird?

Ich hoffe, ich konnte mein Problem verständlich schildern.

Vielen Dank

von Dr. Sommer (Gast)


Lesenswert?

Wie wäre es mit
virtual application* getApp () = 0; in beiden Basis Klassen? Und dann 
ohne Diamant.

von nicht"Gast" (Gast)


Lesenswert?

Moin,


so ganz habe ich nicht verstanden, warum du App unbedingt in den 
Streamklassen brauchst, Das gehört eigentlich nicht dahin. Vielleicht 
ist auch der Name Application irreführend. Klingt, als wäre das die 
übergeordnete Anwendung.


Nur für die Puffergröße ist das aber auch albern. Dann sollte die App 
die Streams als Member haben und von dort stellst du dann den Puffer 
ein.

von Rasputin (Gast)


Lesenswert?

Danke für eure Antworten.

@Dr. Sommer: ich denke mal darüber nach.

nicht"Gast" schrieb:
> Vielleicht
> ist auch der Name Application irreführend.

Gut möglich, ich tu mich mit dem benennen von Klassen und Funktionen 
immer schwer. :(
in der applikation-klasse wird u.A. auch die sample rate gespeichert, 
und geplant sind auch events (z.B. onSampleRateChange) wo sich die 
Objekte registrieren können, um dann entsprechend reagieren zu können 
(Koeffizienten neu berechnen, Puffer vergrössern). Also im Prinzip soll 
alles da rein, was potentiell die ganze Anwendung betrifft, aber nicht 
statisch ist. Vielleicht wäre setting ein passenderer Name.

von tictactoe (Gast)


Lesenswert?

Rasputin schrieb:
> nur wie baue ich jetzt die constructors für die jeweiligen Klassen, dass
> alles korrekt initialisiert wird?

Etwa so:
1
class iostream {
2
protected:
3
   application *app;
4
public:
5
   iostream(application *app) : app(app) {}
6
};
7
class istream : virtual public iostream {
8
// in dieser Klasse wird app benötigt
9
public:
10
   istream(application *app) : iostream(app) {}
11
};
12
class ostream : virtual public iostream {
13
// in dieser Klasse wird app benötigt
14
public:
15
   ostream(application *app) : iostream(app) {}
16
};
17
class test : public istream, public ostream {
18
// in dieser Klasse wird app benötigt
19
public:
20
   test(application *app) : iostream(app) {}
21
   // muss evtl auch so aussehen, hab es nicht getestet:
22
   // test(application *app) :  istream(app), ostream(app), iostream(app) {}
23
};
Insbesonders ist es notwendig, die indirekte Basisklasse iostream vom 
test-Konstruktor aus aufzurufen, weil es eine virtuelle Basisklasse ist 
und test die oberste Klasse in der Hierarchie (sog. "most-derived 
class").

von Rasputin (Gast)


Angehängte Dateien:

Lesenswert?

Habe gerade gemerkt, dass das Problem - wie so oft - gar nicht dort 
steckt, wo ich dachte. Es hat allerdings immer noch mit dem 
diamond-problem zu tun, und zwar dann wenn man diesen weiter vererbt.

das funktioniert:
1
class test : public istream, public ostream {
2
public:
3
  test(audio::application *app_) : istream(app_), ostream(app_), iostream(app_) {
4
  }
5
6
  void process() {
7
    printf("%i", out);
8
  }
9
};

folgendes nicht:
1
class interface : public istream, public ostream {
2
public:
3
  interface(audio::application *app_) : istream(app_), ostream(app_), iostream(app_) {
4
  }
5
};
6
7
class test : public interface {
8
public:
9
  test(audio::application *app_) : interface(app_) {
10
  }
11
12
  void process() {
13
    printf("%i", out);
14
  }
15
};

Fehler:
1
main.cpp: In constructor 'audio::test::test(audio::application*)':
2
main.cpp:83:50: error: no matching function for call to 'audio::iostream::iostream()'
3
   test(audio::application *app_) : interface(app_) {
4
                                                  ^

Im Anhang ist ein kompilierbarer Code.
Mit #define SHOWERROR 1 bzw. 0 könnt ihr zwischen Funktionierend und 
Nicht-funktionierend hin und her schalten.

von tictactoe (Gast)


Lesenswert?

Wie gesagt, wenn man eine virtuelle Basisklasse hat, dann kann man deren 
Initialisierung nicht delegieren, jede "most-derived class" muss es 
immer wieder selbst machen:
1
class test : public interface {
2
public:
3
  test(audio::application *app_) : interface(app_),  iostream(app_) {
4
  }
5
  //...
6
};

von Rasputin (Gast)


Lesenswert?

Danke dir, tictactoe! Funktioniert!

Aber um ehrlich zu sein, verstehe ich die Logik dahinter nicht.
Dass "interface" "iostream" explizit initialisieren muss, kann ich noch 
verstehen. Aber wenn "test" "interface" und "iostream" initialisiert, 
wird "iostream" doch dann zwei mal initialisiert (einmal direkt und 
einmal über "interface")... hmmm...

von Rolf M. (rmagnus)


Lesenswert?

Nein, weil bei virtueller Vererbung immer die am weitesten abgeleitete 
Klasse die virtuelle Basis inizialisiert. Wenn du also ein test 
erzeugst, initialisiert das darin enthaltene interface dann nicht den 
iostream.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.