C++ "deadly diamond of death"

Gast #4747311
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
Angehängte Dateien:
Gast #4747408
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.
Gast #4747441
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.
Gast #4747620
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").
Gast #4747629
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.
Angehängte Dateien:
Gast #4747640
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
};
Gast #4747664
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...

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren