Interface-Methoden mit return-Werten vom Typ des Interface?
-
para schrieb:
An dieser Stelle kommt es nun jedoch vor, dass einzelne Funktionen Objekte vom eigenen Typ zurückliefern
glaub ich dir nicht. dann müsste die funktionsdeklaration nämlich
//statt IComplex cos(); //heißen: ComplexAdapter1 cos(); //bzw. ComplexAdapter2 cos();denn wenn du schreibst, die einzelnen funktionen liefern "objekte vom eigenen typ zurück", musst du in ihnen auch dieses objekt instanziieren
IComplex cos () { //berechnungen... return ComplexAdapter1(...); }das problem ist jetzt folgendes: ComplexAdapter1 wird, weil IComplex eine basisklasse ist, in der funktion ComplexAdapter1::cos automatisch in ein IComplex umgewandelt, und zwar über den copy-constructor IComplex::IComplex(IComplex const&), d.h. alle zusätzliche information, die du ComplexAdapter1 gibst, wird durch die return-anweisung ausgelöscht. nochmal zur veranschaulichung:
ComplexAdapter1 ca; ca.cos()zunächst wird ca instanziiert: d.h. IComplex::IComplex() und danach ComplexAdapter1::ComplexAdapter1() werden aufgerufen. in cos wird ebenfalls ein ComplexAdapter1-objekt konstruiert, d.h. auch dort werden diese beiden funktionen verlassen. mit der "}"-klammer von cos wird jedoch dieses temporäre objekt vernichtet, d.h. IComplex::~IComplex und ComplexAdapter1::~ComplexAdapter1 werden aufgerufen, gleichzeitig wird (versucht) ein neues IComplex *auf basis des danach gleich zerstörten temporären ComplexAdapter1-objekts* herzustellen: IComplex::IComplex(IComplex const&). dieses ist das objekt, das die funktion zurückgibt.
die konsequenz bedeutet für dich: es ist in der tat ein IComplex objekt, das von cos zurück gegeben wird, _es gibt_ kein ComplexAdapter1 objekt mehr, vor allem bedeutet es: du kannst es nicht in ein ComplexAdapter1 objekt casten:
ComplexAdapter1 x = ca.cos();wird nicht hinhauen und
IComplex ic = ca.cos(); dynamic_cast<ComplexAdapter1&>(ic);wird eine exception werfen.
(ein grund mehr, dass anfängern C-style-casts übrigens nicht beigebracht werden sollte.)und du hast jetzt eben das "problem", das IComplex eine rein virtuelle basisklasse ist, bzw. das glück, dass dich der compiler deshalb auf deinen designfehler aufmerksam macht.
/edit: was ist bloß mit meiner rechtschreibung los heute? pfui!
-
para schrieb:
Was mich hier jedoch wundert, ist die erste Zeile - wieso wird ComplexAdapter1::cos() als virtual angegeben/interpretiert?
weil's in ein ersatz für IComplex::cos() ist, und das ist virtuell. die funktionen müssen die gleiche signatur haben. (bis auf den rückgabetyp, weil hier die regeln für die schon genannten kovarianten rückgabetypen einspringen)
-
para schrieb:
lolz schrieb:
Mach es so wie KasF gesagt hat. Das funktioniert (nennt sich covariant return type)
Das ist mir an sich bekannt, und dementsprechend hatte ich das bereits versucht. Allerdings klappt es so nicht wie in meiner Antwort auf seinen Vorschlag zu lesen ist. Zusätzlich zum dort angegebenen Fehler bekomme ich folgenden (Folge-)Fehler:
ComplexAdapter1.h: error: invalid covariant return type for 'virtual ComplexAdapter1 ComplexAdapter1::cos()' IComplex.h: error: overriding 'virtual IComplex IComplex::cos()'Was mich hier jedoch wundert, ist die erste Zeile - wieso wird ComplexAdapter1::cos() als virtual angegeben/interpretiert?
para
Habe vergessen zu erwähnen, dass du Zeiger/Referenzen auf die Objekte zurückgeben musst.
Wenn du die in einen SmartPtr packst, hast du es fast wie zuvor über die Kopie.
-
ich stelle wieder mal ein paar doofe fragen, wenn ihr nichts dagegen habt

warum versuchst du eigentlich die cos sin funktionen in die klasse der komplexen zahlen zu packen? warum denn nicht so:struct myComplexNumber{ float x,y; //evtl n paar konstruktoren //operatorenüberladung, whatever... } /*das interface. Warum machst du das zB nicht so ähnlich wie mit Math.cos() in Java? */ class ComplexMath{ virtual myComplexNumber cos(myComplexNumber c)=0; virtual myComplexNumber sin(myComplexNumber c)=0; virtual myComplexNumber pow(myComplexNumber c)=0; virtual myComplexNumber sinh(myComplexNumber c)=0; virtual myComplexNumber cosh(myComplexNumber c)=0; //usw... die ganzen funktionen halt }; class ComplexMathAdapter1 : public ComplexMath{ //lahme implementierung }; class ComplexMathAdapter2 : public ComplexMath{ //schnelle implementierung };denn... ähm, irgendwie ist mir nicht so ganz klar, warum plötzlich die implementierung der cosinus funktion zu den komplexen zahlen gehören soll.. wenn schon, dann würde ich solche funktionen komplett von allen klassen trennen und als template realisieren, für alles, was eine interne addition/multiplikation sowie eine externe multiplikation mit einem skalar unterstützt... dann bräuchtest du die ganzen funktionen für all deine anderen Körper nicht neuzuschreiben...

naja... wahrscheinlich wieder ein eher sinnloser beitrag, weil ich evtl. das problem gar nicht richtig verstanden habe, aber was solls... :p
sry, wenn's nix nützt
-
warum nicht sinnvoll? es geht doch die ganze zeit schon um richtiges/falsches design.
-
Antwort schrieb:
Es gibt für dein Problem keine Lösung ohne Zeiger/Referenzen....
Und zwar aus dem einfachen Grund, dass ansonsten der Kompiler prinzipiell ein Rückgabeobjekt (mittels CopyCtor) erzeugt ... und das geht nunmal nicht mit abstrakten Klassen.
(Ja - es gibt RVO, aber IIRC prüft der Compiler trotzdem die "Copy-Erzeugbarkeit" des Returntyps).
Nur mit Referenz/Pointer tut der Compiler das nicht (bzw. nur mit dem Pointer selbst) ... und auch nur so kann man überhaupt die Polymorphie (für die mn ja eigentlich das Interface gebaut hat) einsetzen.Typischer Fall von "zuviel Java programmiert"

Gruß,
Simon2.
-
queer_boy schrieb:
para schrieb:
An dieser Stelle kommt es nun jedoch vor, dass einzelne Funktionen Objekte vom eigenen Typ zurückliefern
glaub ich dir nicht. dann müsste die funktionsdeklaration nämlich
//statt IComplex cos(); //heißen: ComplexAdapter1 cos(); //bzw. ComplexAdapter2 cos();Deswegen schrieb ich auch extra "Pseudocode", der nur der Veranschaulichung der Situation dienen sollte. Trotzdem hast du mit diesem Detail natürlich Recht...
queer_boy schrieb:
die konsequenz bedeutet für dich: es ist in der tat ein IComplex objekt, das von cos zurück gegeben wird, _es gibt_ kein ComplexAdapter1 objekt mehr
Das ist völlig ok und beabsichtigt, da es sich hier um einen Adapter handelt, der per Definition (s.o., "Schnittmenge") nicht über mehr Funktionalitäten als die Schnittstelle verfügt. Diese Konformität nach außen ist ja auch der Sinn eines Interfaces.
queer_boy schrieb:
und du hast jetzt eben das "problem", das IComplex eine rein virtuelle basisklasse ist, bzw. das glück, dass dich der compiler deshalb auf deinen designfehler aufmerksam macht.
Als Designfehler sehe ich das nach wie vor nicht an, und die Frage nach dem "richtig/falsch" wird so einfach sicherlich auch nicht beantwortet werden können. Ein "richtig" ist in Sachen Design schließlich auch eine Frage des Kontextes. Es kann durchaus gute Gründe geben Funktionen wie cos() als eine Art indirekte Eigenschaft einer komplexen Zahl (auf Grund des direkten Bezuges zu ihr) zu betrachten und diese somit zu integrieren, um z.B. im Sinne einer Fassade die wahre Komplexität zu kapseln. Ich benötige hier auch keine Templates, da nicht etwa eine generische Funktion cos() im Vordergrund steht, sondern lediglich die komplexe Zahl...
Wenn ich Software-Architekturen entwerfe, gehe ich i.d.R. zunächst rein theoretisch an die Sache heran. Dummerweise ist in diesem speziellen Fall C++ das Problem, da bspw. Java diese Konstruktion ohne Murren zulässt - leider merkt man das eben u.U. erst bei der Realisierung in einer konkreten Sprache

para
-
Simon2 schrieb:
Typischer Fall von "zuviel Java programmiert"

Danke, genau das scheint der Fall zu sein (wahlweise auch c#)

-
Andrey schrieb:
wahrscheinlich wieder ein eher sinnloser beitrag, weil ich evtl. das problem gar nicht richtig verstanden habe, aber was solls...
Absolut nicht sinnlos (sowas gibt's nicht)! In einem anderen Kontext hättest du auch Recht, nur ist der Hintergrund hier eben ein anderer (s. vorletzte Antwort von mir) - und so wie's bislang aussieht komme ich in C++ nicht an diesem Weg vorbei und werde somit ein zweites Interface zum adaptieren der Funktionen auf komplexen Zahlen anbieten...
Dank dir,
para
-
para schrieb:
Wenn ich Software-Architekturen entwerfe, gehe ich i.d.R. zunächst rein theoretisch an die Sache heran. Dummerweise ist in diesem speziellen Fall C++ das Problem, da bspw. Java diese Konstruktion ohne Murren zulässt - leider merkt man das eben u.U. erst bei der Realisierung in einer konkreten Sprache
Nein, Java lässt das auch nicht zu. Weil du verwechselst leider die Syntax und die Bedeutung.
Sowas hat Java garnicht:IComplex cos();Du hast einfach die Syntax aus Java 1:1 auf C++ übertragen, hast aber deren andere Semantik missachtet! Die Syntax in C++ ist eher die hier, wenn man Javas Semantik übertragen will:
IComplex& cos(); // oder IComplex* cos();
-
para schrieb:
Dummerweise ist in diesem speziellen Fall C++ das Problem, da bspw. Java diese Konstruktion ohne Murren zulässt - leider merkt man das eben u.U. erst bei der Realisierung in einer konkreten Sprache
ich hab da mal gestern versucht, ne kleine mathematische spielerei in java zusammenzubasteln, bin beinahe dran verzweifelt, weil es in java nun mal keine zeiger gibt^^ Wie verdammt unterschiedlich die beiden sprachen sind merkt man irgendwie erst dann, wenn man plötzlich von einer zu der anderen wechselt^^

-
@Artchi:
Duh! Ich gebe zu, dass da was dran ist. Mea culpa, vielleicht war's heute einfach zu warm und ich sollte mir die Sache nochmal genauer ansehen...@Andrey:
Da hast ja soo Recht! Als wenn diese beiden dann auch die noch die einzigen wären - ich glaub ich werd langsam alt
-
para schrieb:
...Dummerweise ist in diesem speziellen Fall C++ das Problem, da bspw. Java diese Konstruktion ohne Murren zulässt - leider merkt man das eben u.U. erst bei der Realisierung in einer konkreten Sprache

para
Sagen wir mal so: Wenn Du mit der Sprache richtig vertraut wärst, würdest Du merken, dass das in C++ genauso einfach und korrekt umzusetzen ist und Deine Schwierigkeiten mitnichten an der Sprache liegen

Das C++-Pendant zu Java-Referenzen sind nämlich Pointer ! Wenn Du es nun wie in Java abbilden, aber dabei keine Pointer verwenden möchtest, hast Du Dir einfach selbst ein Bein gestellt.
Ist aber nicht besonders tragisch, weil es einer der typischen Umsteigerfehler ist, wie auch hier zu sehen:
Andrey schrieb:
...weil es in java nun mal keine zeiger gibt...
Einfach falsch !!
Es gibt in Java nichts anderes als Zeiger (und primitive Typen) ! Allerdings besteht da eben deswegen keine Notwendigkeit, diese besonders zu kennzeichnen (im Gegensatz zu C++, wo es noch "Variablen" und C++-Referenzen gibt, weswegen man * und & in der Typenbezeichnung braucht).Gruß,
Simon2.
-
Simon2 schrieb:
Sagen wir mal so: Wenn Du mit der Sprache richtig vertraut wärst, würdest Du merken, dass das in C++ genauso einfach und korrekt umzusetzen ist und Deine Schwierigkeiten mitnichten an der Sprache liegen

Das C++-Pendant zu Java-Referenzen sind nämlich Pointer ! Wenn Du es nun wie in Java abbilden, aber dabei keine Pointer verwenden möchtest, hast Du Dir einfach selbst ein Bein gestellt.
Ist aber nicht besonders tragisch, weil es einer der typischen Umsteigerfehler istOch, damit habe ich auch kein Problem. Wie ich in meinen letzten Posts bereits schrieb, hatte ich an der Stelle einfach ein "Brett vorm Kopp". Mir sind diese Punkte durchaus bekannt, nur sind sie eben länger nicht mehr abgerufen worden. Zum einen wechseln heute recht häufig meine eingesetzten Sprachen (und C++ hatte die längste Pause) und zum anderen code ich heutzutage auch immer weniger, da sich nur noch alles um Architekturen, Prozesse und Steuern von Consultants dreht. Soll heißen: fast alles entsteht als (UML-) Diagramm, und die konkrete Sprache ist "bloß" eine Nebensache...

Dank euch,
para
-
para schrieb:
......
Och, damit habe ich auch kein Problem....... und es ist großes Kino von Dir, das hier so zu sagen.

Danke !Ich hoffe, ich konnte Dir helfen.
Gruß,
Simon2.
-
Simon2 schrieb:
Andrey schrieb:
...weil es in java nun mal keine zeiger gibt...
Einfach falsch !!
Es gibt in Java nichts anderes als Zeiger (und primitive Typen) ! Allerdings besteht da eben deswegen keine Notwendigkeit, diese besonders zu kennzeichnen (im Gegensatz zu C++, wo es noch "Variablen" und C++-Referenzen gibt, weswegen man * und & in der Typenbezeichnung braucht).sry, bin ein noob, und verstehe nicht was du meinst...

Ich bin aber 100% sicher, dass man in java niemals irgendwelche pointer auf primitive datentypen herumschieben darf. Ich kann keiner funktion drei pointer auf floats übergeben, sodass sie diese werte verändert. Und mit "pass by reference" geht es da auch nicht.
Dagegen wird _alles_ was in irgendeiner art und weise von "Object" abgeleitet ist nur über referenzen angesprochen, alle möglichen objekte werden also by-reference-gepasst, was in c++ ja wohl wirklich eher einem zeiger entsprechne würde, sodass auch polymorphie gewährleistet ist...

-
Andrey schrieb:
...
sry, bin ein noob, und verstehe nicht was du meinst...
Hi,
macht ja nix - ist ja jeder erstmal (und Du hast ja auch schon eine Menge gelernt)

Mal sortieren:
Java-Referenzen kann man- ins "Leere" zeigen lassen (null)
- casten,
- mal auf dieses, mal auf jenes Objekt zeigen lassen
- ... und dabei "Polymorphie" und Casting betreiben
- kopieren
- mit "new verwenden".
das trifft exakt so af C++-Pointer zu und im Wesentlichen NICHT auf C++-Referenzen (Polymorphie und ein *new mal außenvor gelassen).
C++-Pointer können zusätzlich- auf "rohen Speicher" zeigen (muss man aber doch ziemlich viel Anstrengung reinstecken)
- Pointerarithmetik auf Arrays (was eine Relikt aus C-Zeiten und den maschinenorientierten C-Arrays geschuldet ist)
Beides Punkte, die man in Java sowieso komplett entfernt hat.
Nichts von alledem kann man in C++ mit "Objektvariablen" (alsoA a;) machen.
Deswegen entsprechen IMO Java-Referenzen eben C++-Pointern (und nicht Referenzen oder Objektvariablen).Dass Java eine Sonderbehandlung für primitive Typen eingeführt hat, finde ich persönlich nicht besonders konsistent und spricht IMO nicht gegen die Analogie.
Außerdem nennt man das, was in Java "call by value" genannt wird, in C++ "call by reference" (eben weil es auch noch einen "value-igeren call" gibt).
Gruß,
Simon2.
-
Hallo zusammen,
das Problem liegt kurz gesagt darin, dass der Copy-Construktor von dem
Objekttyp aufgerufen wird, der zurückgeliefert wird.
Das ist in dem Fall nicht möglich, da die Klasse wegen der pure virtual
Funktion keinen (public) Copy-Construktor besitzt. Eine einfache Lösung das
Problem zu lösen ist (wie KasF) schon gesagt hat, die Rückgabe eines Zeigers oder
einer Referenz. Wegen der Speicher-Verwaltung würde ich die Kapselung in
einen auto_ptr oder shared_ptr vorschlagen. Wenn du den genauen Rückgabe-Typ
des Objektes kennst, dann weißt du auch den ursprünglichen Objekt-Typ und kannst
eine andere Methode außerhalb des Interfaces aufrufen. (Wenn du ne Unterklassi-
fizierung machen möchtest, machst du halt nen Interface, das von IComplex ableitet
und machst da wieder ne pure virtual Funktion rein.)Gruß,
CSpille
-
Simon2 schrieb:
Außerdem nennt man das, was in Java "call by value" genannt wird, in C++ "call by reference" (eben weil es auch noch einen "value-igeren call" gibt).
jo, solangs um objekte geht. Bei primitiven datentypen gibt es in Java dagegen _nur_ das pure call-by-value, das in c++ ja eigentlich dieselbe bedeutung hat. Meine aussage, dass es in java keine pointer gebe bezog sich genau auf diese primitive datentypen.
Und was diese "Lautverschiebung" (nenn ich das mal so^^
) angeht, da hast du natürlich recht, das was in Java einafch "call by reference" genannt wird, entspricht nur in der schreibweise eher c++ referenzen, aber verhalten tuen die sich fast wie c++ pointer. 
so in etwa...

-
Ist das jetzt nicht irgendwie egal, was Java macht und wie das in Java heißt? Für das Lösen der Aufgabe in C++ völlig unerheblich. para muß auch in C++ eine Referenz oder Pointer von IComplex zurück geben, Punkt!