C++: Wieso funktioniert hier dynamic binding nicht?

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • C++: Wieso funktioniert hier dynamic binding nicht?

    Habe hier folgendes Stückchen Code:

    Quellcode

    1. Plot p2("overlay");
    2. int EPN = 3;
    3. double ep[] = {5e-2, 1e-2, 1e-3};
    4. for (int i = 0; i < EPN; i++) {
    5. double *h = rx.map(f, N);
    6. double *l = rx.map(f, N);
    7. haar(h, N, ep[i]);
    8. dehaar(h, N);
    9. linear(l, N, ep[i]);
    10. delinear(l, N);
    11. Line lh(x,h,N);
    12. Line ll(x,l,N);
    13. p2.add(lh);
    14. p2.add(ll);
    15. }
    16. p2.plot();
    Alles anzeigen


    Plot ist eine Klasse die intern eine Liste von "Plottables" hält. Line ist eine Subklasse von Plottable. Plottable definiert getPlotCommand() woraus dann ein Skript für Gnuplot gebastelt wird. Dieses skript wird in Plot::plot() erstellt und ausgeführt. Im obigen Code wird jetzt aber anstatt Line::getPlotCommand() Plottable::getPlotCommand() ausgeführt was eigentlich nur eine dummy Methode ist weil stl-Listen offensichtlich keine abstrakten Klassen als generic type akzeptieren.
    Wenn ich den loop weglasse und einfach die erste Iteration ausführe klappt alles wunderbar.

    Kann mir da jemand sagen wie ich das schnell löse? Bin hier grad ziemlich am verzweifeln. Habe auch schon versucht in Plot::plot() alles mittels dynamic cast nach Line zu casten, hat aber auch nicht geholfen.
    Gruss Dominik.
  • RE: C++: Wieso funktioniert hier dynamic binding nicht?

    Original von deconceptional
    Habe hier folgendes Stückchen Code:

    Quellcode

    1. Plot p2("overlay");
    2. int EPN = 3;
    3. double ep[] = {5e-2, 1e-2, 1e-3};
    4. for (int i = 0; i < EPN; i++) {
    5. double *h = rx.map(f, N);
    6. double *l = rx.map(f, N);
    7. haar(h, N, ep[i]);
    8. dehaar(h, N);
    9. linear(l, N, ep[i]);
    10. delinear(l, N);
    11. Line lh(x,h,N);
    12. Line ll(x,l,N);
    13. p2.add(lh);
    14. p2.add(ll);
    15. }
    16. p2.plot();
    Alles anzeigen


    Plot ist eine Klasse die intern eine Liste von "Plottables" hält. Line ist eine Subklasse von Plottable. Plottable definiert getPlotCommand() woraus dann ein Skript für Gnuplot gebastelt wird. Dieses skript wird in Plot::plot() erstellt und ausgeführt. Im obigen Code wird jetzt aber anstatt Line::getPlotCommand() Plottable::getPlotCommand() ausgeführt was eigentlich nur eine dummy Methode ist weil stl-Listen offensichtlich keine abstrakten Klassen als generic type akzeptieren.
    Wenn ich den loop weglasse und einfach die erste Iteration ausführe klappt alles wunderbar.

    Kann mir da jemand sagen wie ich das schnell löse? Bin hier grad ziemlich am verzweifeln. Habe auch schon versucht in Plot::plot() alles mittels dynamic cast nach Line zu casten, hat aber auch nicht geholfen.


    Den Code den Du zeigst hat mit dem was du schreibst aber nicht wirklich was zu tun oder??
    Ich sehe keine Methode die getPlotCommand heißt. Auch sehe ich keinen Iterator sondern ne einfache for-Schleife, ich sehe auch kein loop. Wenn du die Methoden wie oben gesagt schon virtual anlegst sollte das funktionieren (vtable).

    BTW hat dein Problem mit STL überhaupt nix zu tun
  • RE: C++: Wieso funktioniert hier dynamic binding nicht?

    Plot beinhaltet eine Liste (list<Plottable*> anstatt list<Plottable> <- das STL Problem). Über diese Liste wird in Plot::plot() iteriert und für jedes Element das entsprechende Kommando abgefragt (virtual Plottable::plotCommand() oder so ;) ).

    Mein Problem ist das es in C++ offensichtlich einen unterschied macht ob ich

    Quellcode

    1. Line l(...);
    2. p.add(l);
    3. p.plot();

    oder

    Quellcode

    1. for (...) {
    2. Line l(...);
    3. p.add(l);
    4. }
    5. p.plot();

    schreibe.

    Bei ersterem funktioniert dynamic binding, beim zweiten nicht... was läuft da schief?
    Gruss Dominik.
  • RE: C++: Wieso funktioniert hier dynamic binding nicht?

    Original von deconceptional
    Plot beinhaltet eine Liste (list<Plottable*> anstatt list<Plottable> <- das STL Problem). Über

    Warum machst Du das auch so, ergibt doch überhaupt kein Sinn. Du machst ja beim speichern ein "echtes" Plottable Objekt daraus.

    Es ist ein Wunder, dass es überhaupt funktioniert. Dass es in einem Fall klappt, erscheint mir eher zufällig.
    C++
  • Poste bitte mal die Signatur von Plot::add().
    getPlotCommand muss auf jeden Fall virtuell sein, und Plot::add muss entweder einen Zeiger oder eine Referenz auf eine Plottable-Instanz bekommen. Da Du Line wahrscheinlich per value übergibst, läuft grundsätzlich was falsch (Polymorphismus geht in C++ nur über Zeiger oder Referenzen). Du übergibst die lokale Line-Instanz vom Stack aus der Schleife, nach dem Schleifendurchlauf existiert dieses aber nicht mehr (ich vermute, Du hast keine entspr. Copy-Konstruktoren und einen new-operator für Line geschrieben, die das abfangen).
    Deine Liste sollte auf jeden Fall mit Zeigern auf Plottable-Instanzen arbeiten (geht auch gar nicht anders) und Plottable sollte abstrakt sein.
  • RE: C++: Wieso funktioniert hier dynamic binding nicht?

    Original von zermelo
    Original von deconceptional
    Plot beinhaltet eine Liste (list<Plottable*> anstatt list<Plottable> <- das STL Problem). Über

    Warum machst Du das auch so, ergibt doch überhaupt kein Sinn. Du machst ja beim speichern ein "echtes" Plottable Objekt daraus.

    Es ist ein Wunder, dass es überhaupt funktioniert. Dass es in einem Fall klappt, erscheint mir eher zufällig.


    Ok, ich entnehme daraus das ich mit einer Zuweisung Typ1 var = wasVomTyp2 eine Konvertierung stattfindet?
    Gruss Dominik.
  • Original von SumpfMonster
    Poste bitte mal die Signatur von Plot::add().
    getPlotCommand muss auf jeden Fall virtuell sein, und Plot::add muss entweder einen Zeiger oder eine Referenz auf eine Plottable-Instanz bekommen. Da Du Line wahrscheinlich per value übergibst, läuft grundsätzlich was falsch (Polymorphismus geht in C++ nur über Zeiger oder Referenzen). Du übergibst die lokale Line-Instanz vom Stack aus der Schleife, nach dem Schleifendurchlauf existiert dieses aber nicht mehr (ich vermute, Du hast keine entspr. Copy-Konstruktoren und einen new-operator für Line geschrieben, die das abfangen).
    Deine Liste sollte auf jeden Fall mit Zeigern auf Plottable-Instanzen arbeiten (geht auch gar nicht anders) und Plottable sollte abstrakt sein.


    Ok, jetzt funktioniert das ganze, vorher war Plot::add(Plottable&) und jetzt ist Plot::add(Plottable*) was aber, wenn ich mich gerade richtig informiert habe, keinen Unterschied macht. Was ich nach wie vor nicht verstehe ist, wieso das mit den Variabeln auf dem Stack nicht geht. Ich hab die Linien jetzt mit new alloziert, damit hab ich keine Probleme mehr.
    Gruss Dominik.
  • RE: C++: Wieso funktioniert hier dynamic binding nicht?

    Original von deconceptional
    Ok, ich entnehme daraus das ich mit einer Zuweisung Typ1 var = wasVomTyp2 eine Konvertierung stattfindet?


    Konvertierung muss ja stattfinden, sonst klappt die Zuweisung nicht. Wenn Du nur Zeiger zuweist, ist das nicht problematisch (bei Klassenhierarchien), weisst Du aber direkt die Objekte zu, werden diese implizit umgewandelt, sofern möglich.

    Zur Illustration:

    Quellcode

    1. struct Foo {
    2. int feld1;
    3. };
    4. struct Bar : public Foo {
    5. int feld2;
    6. };
    7. int main(int argc, char* argv[]) {
    8. Bar baz;
    9. baz.feld1 = 666;
    10. baz.feld2 = 999;
    11. Foo foz;
    12. foz = baz;
    13. // wo ist jetzt feld2 gespeichert?
    14. // genau! NIRGENDWO
    15. Bar *b = static_cast<Bar*>(&foz);
    16. // Natuerlich kein Problem:
    17. std::cout << b->feld1 << std::endl;
    18. // Geht schief: feld2 ist UNDEFINIERT
    19. std::cout << b->feld2 << std::endl;
    20. return 0;
    21. }
    Alles anzeigen


    EDIT:
    Achso, das Gegenbeispiel, dass alles mit Pointern kein Problem ist:

    Quellcode

    1. /* Gegenbeispiel */
    2. Foo *spam = &baz;
    3. Bar *eggs = static_cast<Bar*>(spam);
    4. /* nur zeiger geschubbst, das objekt ansich
    5. * ist aber unveraendert geblieben. folglich
    6. * klappt alles: */
    7. std::cout << eggs->feld1 << std::endl;
    8. std::cout << eggs->feld2 << std::endl;
    9. // added comfort: (wer mag schon diese pfeile...)
    10. Bar &eggywags = *static_cast<Bar*>(spam);
    11. std::cout << eggywags.feld1 << std::endl;
    12. std::cout << eggywags.feld2 << std::endl;
    Alles anzeigen
    C++
  • Original von deconceptional
    Ok, jetzt funktioniert das ganze, vorher war Plot::add(Plottable&) und jetzt ist Plot::add(Plottable*) was aber, wenn ich mich gerade richtig informiert habe, keinen Unterschied macht. Was ich nach wie vor nicht verstehe ist, wieso das mit den Variabeln auf dem Stack nicht geht.

    Es geht mit Variablen auf dem Stack, aber dazu muß man einiges beachten. Unter anderem das von zermelo angesprochene Slicing muß man verhindern.

    C-Quellcode

    1. #include <string>
    2. #include <iostream>
    3. class Base {
    4. std::string name_;
    5. public:
    6. Base (std::string name) : name_(name) {}
    7. void virtual print () {
    8. std::cout << this->name_ << "\n";
    9. }
    10. // Abstrakte Klasse, wichtig ist daß er virtuell ist, sonst darf man über
    11. // Basiszeiger keine Objekte löschen.
    12. virtual ~Base() = 0;
    13. };
    14. Base::~Base() {}
    15. class A : public Base {
    16. public:
    17. A () : Base ("Klasse A") {}
    18. ~A() {}
    19. };
    20. class B : public Base {
    21. public:
    22. B () : Base ("Klasse B") {}
    23. ~B() {}
    24. };
    25. int main () {
    26. A a;
    27. B b;
    28. a.print ();
    29. b.print ();
    30. Base* p1(&a);
    31. Base* p2(&b);
    32. p1->print();
    33. p2->print();
    34. }
    Alles anzeigen

    Wichtig ist hierbei, daß alle Methoden virtuell deklariert sein müssen, sonst geht das nicht. Einfaches Überladen reicht nicht aus.

    C-Quellcode

    1. include <string>
    2. #include <iostream>
    3. class Base {
    4. std::string name_;
    5. public:
    6. Base (std::string name) : name_(name) {}
    7. void print () {
    8. std::cout << this->name_ << "\n";
    9. }
    10. virtual ~Base() = 0;
    11. };
    12. Base::~Base() {}
    13. class A : public Base {
    14. public:
    15. A () : Base ("Klasse A") {}
    16. ~A() {}
    17. };
    18. class B : public Base {
    19. public:
    20. B () : Base ("Klasse B") {}
    21. ~B() {}
    22. void print () {
    23. std::cout << "uups\n";
    24. }
    25. };
    26. int main () {
    27. A a;
    28. B b;
    29. a.print ();
    30. b.print ();
    31. Base* p1(&a);
    32. Base* p2(&b);
    33. p1->print();
    34. p2->print();
    35. }
    Alles anzeigen


    Nächster Punkt:
    Alle Container der STL basieren auf Wertsemantik, daher sollte man niemals Objekte über Zeiger oder Referenzen dort ablegen, wenn sie nicht anderweitig verwaltet werden!

    Damit das letzte Beispiel funktioniert brauchst Du boost -> boost.org

    C-Quellcode

    1. include <string>
    2. #include <iostream>
    3. #include <vector>
    4. #include <algorithm>
    5. #include <boost/smart_ptr.hpp>
    6. class Base {
    7. std::string name_;
    8. public:
    9. Base (std::string name) : name_(name) {}
    10. void virtual print () {
    11. std::cout << this->name_ << "\n";
    12. }
    13. virtual ~Base() = 0;
    14. };
    15. Base::~Base() {
    16. std::cout << "Base Destruktor called\n";
    17. }
    18. class A : public Base {
    19. public:
    20. A () : Base ("Klasse A") {}
    21. ~A() {}
    22. };
    23. class B : public Base {
    24. public:
    25. B () : Base ("Klasse B") {}
    26. ~B() {}
    27. };
    28. void print (Base* base) {
    29. base->print();
    30. }
    31. void print_v (boost::shared_ptr<Base> p) {
    32. p->print();
    33. }
    34. int main () {
    35. std::vector<boost::shared_ptr<Base> > v;
    36. std::vector<Base*> vector(2);
    37. {
    38. A a;
    39. B b;
    40. vector[0] = &a;
    41. vector[1] = &b;
    42. std::for_each (vector.begin(), vector.end(), print);
    43. }
    44. // Ab hier darf man nicht mehr die Zeiger in vector nutzen,
    45. // da die Objekte bereits zerstört wurden!
    46. std::cout << "Block eins beendet\n";
    47. {
    48. v.push_back (boost::shared_ptr<Base> (new A));
    49. v.push_back (boost::shared_ptr<Base> (new B));
    50. }
    51. std::for_each (v.begin(), v.end(), print_v);
    52. // so und nun tun wir mal genau das was man nicht tun darf.
    53. std::for_each (vector.begin(), vector.end(), print);
    54. }
    Alles anzeigen