Entwickler-Ecke

Off Topic - [C++] Mehrfachvererbung


Aya - Mo 09.06.08 17:25
Titel: [C++] Mehrfachvererbung
Hi,

ich hab ne frage zu C++ und der möglichkeit der Mehrfachvererbung.. evtl kann mir ja jemand hier helfen :)
(Es geht um C++ nicht C#.. ich benutze nur die C# code-tags ;) )


Also,
ich habe 3 Klassen:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
class Main {
public:
  void helloWorld();
};


class A {
public:
  void doSomething(int x);
};

class B {
public:
  void doNothing(string s);
};


und jetzt eine dritte klasse die IMMER von Main ableitet, und zusätzlich manchmal von A und/oder B:


C#-Quelltext
1:
2:
3:
4:
class AB : public Main, public A, public B {
public:
  .....
};


soweit kein problem, aber..
Im code werden die klassen alle als array von Main* gespeichert, aber wenn ich jetzt gern auf die doNothing() funktion zugreifen möchte, wie mach ich das?

Ich ging davon aus, dass ich einfach:


C#-Quelltext
1:
((B*)myClass)->doNothing(..);                    


verwenden kann, aber das klappt leider nicht, also es kommt dann wohl irgendwie intern durcheinander und ruft falschen code auf...

wenn ich stattdessen schreibe:


C#-Quelltext
1:
((AB*)myClass)->doNothing(..);                    


geht es, aber die lösung ist für mich eher unpraktisch, da ich halt wie gesagt manchmal auc hnur von A oder B ableite und dementsprechend nicht immer in AB* casten kann...

Die einzige lösung die mir einfiele wären viele if abfragen die überprüfen um welchen typ es sich handelt und dann dementsprechend casten...

Gibt's da irgendwas anderes, besseres?

Aya~

PS: In dem moment wo ich die doNothing() funktion aufrufe weiß ich sicher das das objekt von B geerbt hat, aber ich weiß nicht sicher ob es auch von A geerbt hat = ich kann nicht sicher sagen ob ich nach AB* oder nach BOnly* casten muß..


tommie-lie - Di 10.06.08 11:56

Folgender Code funktioniert bei mir (g++ (GCC) 4.2.3 (Ubuntu 4.2.3-2ubuntu7)):

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
#include <iostream>

class Main {
public:
  void helloWorld() { std::cout << "Main::helloWorld" << std::endl; };
};

class A {
public:
  void doSomething(int x) { std::cout << "A::doSomething" << std::endl; };
};

class B {
public:
  void doNothing(float s) { std::cout << "B::doNothing" << std::endl; };
};

class AB : public Main, public A, public B {
};


int main() {

  Main *ab = new AB();
  ab->helloWorld();
  ((A*)(ab))->doSomething(23);
  ((B*)(ab))->doNothing(42);

  return 0;
}
und liefert folgende Ausgabe:

Quelltext
1:
2:
3:
4:
thomas@Majestix:/tmp$ ./a.out 
Main::helloWorld
A::doSomething
B::doNothing

Mehr Typsicherheit erhältst du allerdings durch eine polymorphe Vaterklasse und dynamic_casts:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
#include <iostream>

class Main {
public:
  void helloWorld() { std::cout << "Main::helloWorld" << std::endl; };
  virtual ~Main() {}; // oder beliebige andere virtuelle Methode
};

class A {
public:
  void doSomething(int x) { std::cout << "A::doSomething" << std::endl; };
};

class B {
public:
  void doNothing(float s) { std::cout << "B::doNothing" << std::endl; };
};

class AB : public Main, public A, public B {
};


int main() {

  Main *ab = new AB();
  ab->helloWorld();
  dynamic_cast<A*>(ab)->doSomething(23);
  dynamic_cast<B*>(ab)->doNothing(42);

  return 0;
}
Getestet und für gut befunden auf dem gleichen System, durch den dynamic_cast sollte auch sichergestellt werden, daß die Methoden tatsächlich existieren, bevor du sie aufrufst.

Wenn A und B selbst wiederum von einer anderen Klasse erben, möglicherweise von einer gemeinsamen Oberlkasse von Main, interessiert dich vielleicht http://parashift.com/c++-faq-lite/multiple-inheritance.html#faq-25.8 und http://parashift.com/c++-faq-lite/multiple-inheritance.html#faq-25.9.


Aya - Di 10.06.08 12:14

Yay mit dynamic_cast klappt es, tausend dank!! :)