Klassenfrage :)
-
Hallo und guten Tag,
Ich programmiere ein in c von mir selbst geschriebenes Programm in c++ um, weil es mir in c zu komplex ist und ich auf die Objektorientiertheit von c++ setzte. Nun meine Frage:
Ist es möglich einem Objekt einen Funktionszeiger auf eine Funktion des Hauptprogramms (in dem die main Funktion definiert ist) zu übergeben, so das ich in diesem Objekt Methoden anderer Klassen aufrufen kann; oder muss ich mein Objektdesing ändern?Z.B.: Ein Objekt der Klasse Wurm ist einem Objekt der Klasse Apfel übergeben worden und soll Methoden der Klasse Apfel aufrufen. Abre der Apfel soll hauptsächlich Methoden des Wurmes Aufrufen
-
Ja, klar geht das. Was aber wichtig ist und worüber du dir offensichtlich noch keine Gedanken gemacht hast: Auf welchem Objekt soll der Aufruf erfolgen? Für ein bestimmtes Objekt, das der Nutzer bei der Übergabe entscheidet? Für ein Objekt das der Aufrufer bestimmt und der Nutzer hat nur gesagt, welche Funktion?
#include <iostream> #include <functional> class Foo { int i; public: Foo(int i) : i(i) {} void func1() { std::cout << "Func 1 von Foo(i=" << i << ")\n"; } void func2() { std::cout << "Func 2 von Foo(i=" << i << ")\n"; } }; class Caller { void (Foo::* unbound_foo_member)(); std::function<void()> bound_member; public: Caller( void (Foo::*unbound_foo_member)(void), std::function<void()> bound_member) : unbound_foo_member(unbound_foo_member), bound_member(bound_member) {} void call() { Foo f(123); (f.*unbound_foo_member)(); bound_member(); } }; int main() { Foo f(456); std::function<void()> bound_func1 = [&f] {f.func1(); }; Caller c(&Foo::func2, bound_func1); c.call(); }
@EL-europ sagte in Klassenfrage :
Z.B.: Ein Objekt der Klasse Wurm ist einem Objekt der Klasse Apfel übergeben worden und soll Methoden der Klasse Apfel aufrufen. Abre der Apfel soll hauptsächlich Methoden des Wurmes Aufrufen
Klingt aber trotzdem als ob da ein Fehler im Design ist, wenn Objekte hauptsächlich die Methoden anderer Klassen aufrufen und umgekehrt. Es ist äußerst ungewöhnlich, dass man das überhaupt hat, noch viel mehr wenn das etwas ist, was häufig bei dir vorkommt. Du merkst ja sicher auch, das der Code dort oben ziemlich unschön ist und nicht so aussieht, wie übliche C++ Programme (zumindest nicht wie übliches C++ im Jahr 2024, vor 30 Jahren kann das teils so ausgesehen haben).
-
@EL-europ sagte in Klassenfrage :
Ich programmiere ein in c von mir selbst geschriebenes Programm in c++ um, weil es mir in c zu komplex ist und ich auf die Objektorientiertheit von c++ setzte.
Oh schade, und was machst du nun aktuell? Oder war die Vergangenheitsform nur ein Versehen/Rechtschreibfehler?
-
@omggg
Ich hab nee Idee für ein Programm und wollte c lernen, nur werden mir jetzt die c- Dateien zu Umfangreich. Und ich denke mit einem objektorientierten Ansatz besser zu fahren. Die Antwort von SeppJ bin ich grad am versuchen zu verstehen und zu übertragen. Wird wohl morgen werden bis ich selbst neue Erkenntnis erlangt habe
-
oder ... verabschiede dich vom alten C++ und nimm was Neueres, was nicht obsolet ist. Das Entwicklungsrad steht ja nicht still ... andere OO-Programmiersprachen haben weniger Boilerplate/Overhead und dafür mehr "Zucker". Das soll dann auch helfen, Spaghetticode zu vermeiden.
-
@omggg
Ich hab das Prog vor Corona in python geschrieben, aber die Grafikausgabe war zu langsam (Darstellung der Änderung der Mandelbrondtmenge über die "Iterationstiefe") und die "quick n dirty" lösungen wurde immer mehr. Dann hab ich mit diesen RPI-Pico boards und dessen c-sdk gespielt.Vor ner woche hab ich das Mandelbrodt zur hälfte in c geschrieben und die Dateien sind mir zu lang (Aber die Grafik über Framebuffer ist schnell genug). Natürlich ist es so das ich c auch nur unzureichend beherrsche und es vielleicht auch mit python schneller ginge, aber das Leben ist ein Geschenk
Entwickeln tue ich auf einem Raspi 5 mit Geany/Kommandozeile.
Ich mach mir erst mal "tiefgehendere" Gedanken über ein Klassendesing von Mandelbrodt (Die GUI Ist selbstgemacht), Und werde die entsprechenden Kapitel in Jürgen Wolf's c++11 lesen. Oder gibt es eine Onlninefom die vielleicht sogar besser ist.
Herr Wolf ist in diesem Buch immer sehr theoretisch mit seinen Beispielen.
-
@omggg
Um etwas genauer zu sein:
Es gibt die Klasse Display mit Methoden um in den FB zu Zeichnen und zu Schreiben. Ausschließlich ein Objekt dieser Klasse wird einem Objkt klasse ui_interface übergeben das sich selbst darüber darstellt. Das Objekt der Klasse ui_interface wird einem Objekt der Klasse Apple Übergeben das die berechnung der Mandelbrodtmenge durchführt und diese über die Methode "ui_interface->diplay->putPixel" darstellt. Die Parameter der Berechnung ist das Objekt ui_interface. Und auch die Buttons sind in ui_iterface. Dort genau komme ich nicht weiter, eine Apple->Methode aufrufen wenn in ui-interface::onMouseOver x und y passen. (Die Mausfunktion ist nicht das Problem sondern der Methodenaufruf)
-
@SeppJ
Danke für die Antwort, aber ich steige leider nicht dahinter was die Definition von Caller bedeuted. Wie du selbst schon andeutest ist das ziemlich umständlich.
-
Ist nur ein Beispiel für die beiden Modi, die ich erklärt habe:
unbound_foo_member
ist ein Zeiger auf eine Methode der anderen KlasseFoo
und der Nutzer dieses Zeigers muss selber irgendwo ein Foo-Objekt herbekommen (hier erstellt der Caller dafür das Objekt mit i=123)bound_member
ist ein allgemeiner Zeiger auf eine beliebige Funktion (mit der passenden Signatur), und man hat ihm hier ein funktionsartiges Objekt zugewiesen, das den Aufruf an das Foo-Objekt (i=456) aus der main koppelt. Man kann dem gar nicht mehr ansehen, dass das irgendwie mit Foo zu tun hat, es ist nicht unterscheidbar von einem normalen Funktionszeiger (außer dass der Typ dahinter irgendetwas wildes ist)
Dass ich das in die Klasse
Caller
verpackt habe, kommt wegen deiner Fragestellung, dass du das unbedingt einer Klasseninstanz übergeben wolltest. Das Prinzip ist aber völlig unabhängig davon, wie man es benutzt.void (Foo::*)(void)
mag zwar kompliziert aussehen, aber ist auch nur ein Typ wie jeder andere Variablentyp und nutzt sich genauso wieint
.Ich möchte aber nochmals betonen, dass das äußerst ungewöhnlich ist, so viel (oder überhaupt irgendetwas) in C++ mit Memberfunktionszeigern machen zu wollen, und du dein Design nochmal überdenken solltest. In C kann ich das ja noch sehen (und da sind es ja auch keine richtigen Memberfunktionen), um so etwas wie virtuelle Funktionen zu emulieren. Ist es vielleicht das, was du wirklich willst? Virtuelle Funktionen? Und hast noch nicht davon gehört weil du neu in C++ bist?
omggg ist unser örtlicher Dauertroll. Ich würde an deiner Stelle dessen Beiträge einfach komplett ignorieren.
-
@SeppJ
Haha, immerhin half mir der Troll meine Frage etwas zu konkretisieren.
Ich meine: Ist es möglich Apple->schrumpf() aus Apple->Wurm->friss() aufzurufen? und, würdest du bitte eine der beiden Möglichkeiten nochmals isoliert für mich dartellen, wenn nicht gar die Antwort auf die Frage?
Ich verstehe vor allem die Definition Von Caller(...) unter public nicht
-
@EL-europ
Ich bin kein Programm, aber @SeppJ mag mich nicht, deshalb schreibt er oder sie das.Auf deine Fragen kann ich zurzeit aber nicht näher eingehen.
-
@omggg
ok, danke
-
@SeppJ Ichversuch mal deinen Code zu beschreiben
in der Klasse Caller
wird mit "void (foo.." ein Zeigerobjekt auf irgendeine Methode der Klasse Foo angelegt,
und mit "std::func.."eine std::function mit dem namen "bound_member" und dem Rückgabewert void deklariert.
Dann unter public folgt der Konstruktor von Caller dem zwei Parameter und dazu zwei ppassende Standartwerte übregeben werden? Und in der Methode call() wird "einfach" auf den Konstruktor von Foo zugegriffen um dann über das Objekt f.*unbound_foo_member auf eine in den Konstrutionsparametern übergebene Funktion in Foo zugreifen zu können und dann wird die std::function bound_member aufgerufen.Wenn das stimmt ist das mir zu Abtrakt um es auf Apfel und Wurm übertragen zu können.
Kannst du bitte den Konstruktor von Caller( wenn es den überhaupt einer ist) und die "Schritte" in main näher erläutern
-
Da, ganz ohne drumherum.
#include <iostream> #include <functional> using namespace std; class Foo { int i; public: Foo(int i) : i(i) {} void func1() { std::cout << "Func 1 von Foo(i=" << i << ")\n"; } }; int main() { Foo f(456); std::function<void()> bound_func1 = [&f] {f.func1(); }; void (Foo::* unbound_func1)() = &Foo::func1; bound_func1(); (f.*unbound_func1)(); }
Den Caller hatte ich doch nur gezeigt, weil du dich so daran aufgehangen hattest, dass das unbedingt in einer Klasse sein müsse, als ob da dran irgendetwas besonderes wäre.
-
@SeppJ
Ich hab eine einfache Frage ja auch kompliziert gestellt, und versuch jetzt erstmal die Übertragung deines Codes
-
@EL-europ sagte in Klassenfrage :
@SeppJ
Ich hab eine einfache Frage ja auch kompliziert gestellt, und versuch jetzt erstmal die Übertragung deines CodesNein! Werd' lieber dein Design los, das dich denken lässt, dass dies überhaupt nötig wäre! Du übersiehst garantiert irgendwas simples, das schon in der Sprache eingebaut ist. Dazu musst du natürlich erst einmal die Sprache besser lernen. Aber das wäre was gutes, denn man sollte schließlich die Sprache beherrschen, die man spricht.
-
@SeppJ
Mir fällt nix besseres ein. Ich müsste ja eigentlich eine "abstrakte" Klasse definieren von der ich Apfel und Wurm gemeinsam ableite; Wenn ich den oop-ansatz richtig verstehe?
-
@EL-europ sagte in Klassenfrage :
@SeppJ
Mir fällt nix besseres ein. Ich müsste ja eigentlich eine "abstrakte" Klasse definieren von der ich Apfel und Wurm gemeinsam ableite; Wenn ich den oop-ansatz richtig verstehe?Da wird ja gerade der Designfehler sein, denn das klingt offensichtlich falsch. Aber mit dem Apfel-Wurm-Beispiels kommt man da nicht weiter, weil da in der echten Welt keine tiefergehenden Beziehungen existieren, als das beides physikalische Objekte sind, und das ein Apfel einen oder mehr Würmer haben kann. In der echten Welt wird der Apfel nie etwas mit einem Wurm machen, und die Würmer werden eventuell etwas aus dem umgebenden Apfel fressen. Nichts davon klingt auch nur entfernt nach Übergabe von Memberfunktionszeigern..
-
@SeppJ Nun ja aber:
Die Beziehung "Apfel->schrumpf() wenn Apfel->Wurm->friss()" ist gegeben.
Aber du hast Recht, ich sollte zumindest die entsprechenden Kapitel noch mal lesen. Bis dahin
-
Ist ein bisschen komisch, dass der Apfel sich von dem Wurm fressen lässt, aber naja, nehmen wir das mal als gegeben an, weil die Analogie vielleicht nicht perfekt ist. Aber wieso muss in dem Fall an
Wurm::friss
die MethodeApfel::schrumpf
übergeben werden? Sollte nicht die Fressmethode wissen, dass sie den Apfel schrumpfen soll?class Wurm { void friss(Apfel &zu_fressender_apfel) { ... zu_fressender_apfel.schrumpf(); ... } };
Oder ist die Sache, dass der Wurm auch in anderem Obst sein kann? Dann wäre das, wie spekuliert, ein Fall für virtuelle Methoden:
class Obst { virtual void schrumpf(); }; class Apfel: public Obst { virtual void schrumpf() {wie ein Apfel schrumpft;} }; class Birne: public Obst { virtual void schrumpf() {wie eine Birne schrumpft;} }; class Wurm { void friss(Obst &zu_fressendes_obst) { ... zu_fressendes_obst.schrumpf(); ... } };
Da wird dann in
friss
automatisch an die passendeschrumpf
-Methode verwiesen, je nachdem, welches Obst der Wurm frisst.Im Hintergrund arbeiten dann zugegebenermaßen Memberfunktionszeiger. Aber das macht halt die Sprache für dich als Feature, und du musst dich nicht mit solch fehleranfälligem Zeug mit komischer Syntax herumschlagen. In C muss man das halt selber machen, wenn man diesen Effekt will, daher hatte ich die Vermutung, dass das hier die Richtung ist, aus der du kommst.