Performancemythen?
-
Helium schrieb:
Xin schrieb:
Helium schrieb:
Xin schrieb:
Beschreibe den Ablauf von 'dynamisch dispatched' mal ganz detailiert.
Meinst du den technischen Ablauf? Der ist doch von Sprache zu Sprache extrem verschieden. In statisch typisierten Sprachen habe ich ganz ander Optimierungsmöglichkeiten, z.B. mit virtuellen Methodentabellen, als z.B. in dynamisch typisierten Sprachen. Wenn ich Multimethoden unterstütze kann ich wieder anders vorgehen, jenachdem, wieviel Typinformation mir zur Compilezeit zur verfügung steht. ...
Bei objektorientiertem Problemstellungen ist der Punkt ja eben, dass die exakte Typ-Information eben nicht zur Compilezeit zur Verfügung steht. Darum OOP, weil Multimethoden eben nicht reichen.
Multimethoden dispatchen auch erst zur Laufzeit, da dort auch erst zur Laufzeit die Typ-Information vorhanden ist und nicht zur Compilezeit. Darum geht es doch gerade bei Multimethoden.
Korrekt. Aber das ist doch nur bedingt objektorientiert, da die möglichen Objekttypen zur Compilezeit bekannt sein müsen.
Diese Technik hat man mit virtuellen Funktionen (Edit: "OOP" entfernt) ja eben entfernen wollen, schließlich wurden so in C die Fehler produziert.Helium schrieb:
(defmethod collide-with ((x asteroid) (y asteroid)) ;; deal with asteroid hitting asteroid ) (defmethod collide-with ((x asteroid) (y spaceship)) ;; deal with asteroid hitting spaceship ) (defmethod collide-with ((x spaceship) (y asteroid)) ;; deal with spaceship hitting asteroid ) (defmethod collide-with ((x spaceship) (y spaceship)) ;; deal with spaceship hitting spaceship )
entspricht:
void collide_with( SpielObjekt & lhs, SpielObjekt & rhs ) { if( typeid( lhs ) == typeid( asteroid ) && typeid( rhs ) == typeid( asteroid ) ;; deal with asteroid hitting asteroid if( typeid( lhs ) == typeid( asteroid ) && typeid( rhs ) == typeid( spaceship ) ;; deal with asteroid hitting spaceship if( typeid( lhs ) == typeid( spaceship ) && typeid( rhs ) == typeid( asteroid ) ;; deal with spaceship hitting asteroid if( typeid( lhs ) == typeid( spaceship ) && typeid( rhs ) == typeid( spaceship ) ;; deal with spaceship hitting spaceship }
Sobald man Raketen hinzufügt, muss collide_with verändert werden und collide-with kann auch nicht mehr auflösen.
Löst man das ganze mit OOP, also virtuellen Funktionen auf, ergibt sich an collide_with keine Änderung.Erkennst Du nun den Unterschied?
EDIT: Ich möchte noch hinzufügen, dass Multimethoden sich natürlich am Objekt orientieren => sie sind objekt orientiert. Sie stellen allerdings eine begrenzte Technik dar, die - weil die Typen zur Compilezeit bekannt sein müssen. Sie ist also davon abhängig, dass die Anzahl der möglichen Typen sich nicht verändert.
In einer Library sind sie nicht verwendbar.Über eine virtuelle Funktion lässt sich eine das ganze Problem besser lösen:
void collide_with( SpielObjekt & lhs, SpielObjekt & rhs ) { lhs.collide_width( rhs ); }
Asteriod kann mit Asteriod kollidieren. Kollidiert er mit etwas anderem, lässt er sich mit dem anderen kollidieren.
Spaceship kann mit Asteriod und Spaceship kollidieren, sonst kollidiert es mit dem anderen.Erweitert man das Programm nun mit Raketen, so beschreibt man in Rakete, wie eine Rakete mit Asteroiden, Spaceships und Raketen kollidiert. Ansonsten sind nirgendwo Änderungen im Code erforderlich, weil der Code sich vollkommen am Objekt orientieren kann, ohne vom Typ des Objektes abhängig zu sein.
Einfache OOP-Techniken, wie diese, haben in C viele Fehler verursacht, weil an einzelnen Stellen vergessen wurde, den Code zu erweitern.
-
Xin schrieb:
Sobald man Raketen hinzufügt, muss collide_with verändert werden und collide-with kann auch nicht mehr auflösen.
Löst man das ganze mit OOP, also virtuellen Funktionen auf, ergibt sich an collide_with keine Änderung.Erkennst Du nun den Unterschied?
falsch. Wenn du vererbung verwendest, musst du jede Klasse verändern, wenn du Rakete hinzufügst. das ist double dispatching, und die OOP bietet dafür keine Lösung. Ausser natürlich, für die 3 Objekte reicht derselbe CollideWith algorithmus. Aber wenn man annimmt, dass Asteroid ein kreis, raumschiff ein quadrat und rocket ein Strich ist, dann brauchst du für jede kombination eine eigene methode. Und anstatt dann an einer Stelle einfach die neuen funktionen hinzuzufügen, musste es dann in jeder Klasse machen. dazu kommt, dass du dich ab dann noch damit rumschlagen kannst, dass in Raumschiff Rakete noch nicht definiert ist und du dann nochmal irgendwo ne vorwärtsdeklaration brauchst(in C++ natürlich). Ohja, nicht zu vergessen, dass du dann noch das Problem hast, dass jede Klasse erstmal rauskriegen muss, welchen genauen typ nun das übergebene objekt hat, damit dann die endgültige methode aufgerufen werden kann.
Das Ergebnis ist dann, dass du nichtmehr eine dispatch funktion hast, sondern für jede Klasse eine(nämlich singledispatch):
class Asteroid; class SpaceShip; Class Rocket:public Object { public: virtual bool collide(const Object* o)const { if(typeid(o)==typeid(Asteroid)) { //collide with asteroid } if(typedid(o)==typeid(SpaceShip) { //collide with SpaceShip } if(typedid(o)==typeid(Rocket) { //collide with Rocket } } }; //selbes für Asteroid und Spaceship ... bool collideWith(const Object* o1,const Object* o2) { return o1->collide(o2); }
und wenn du jetzt einen Typ hinzufügst, kann keine dieser Dispatchfunktionen mehr auflösen, du musst also nicht nur die neuen Funktionen hinzufügen, nein du musst auch noch alle dispatchfunktionen anpassen(mit C++ und statischer Polymorphie ist das übrigens nur ein ",Typename" in der Typliste, dann ist der komplette doubleDispatcher wieder up to date ;)).
Erkennst du den unterschied?
Aber wozu mach ich mir die Mühe? mir antwortest du ja eh net
-
Xin schrieb:
asc schrieb:
Nachtrag: Ein Hoch auf Bjarne Stroustrup, und seinen einfachen und kleinen Satz: "That said, object-oriented programming is a style of programming originating with Simula (about 40 years ago!) relying of encapsulation, inheritance, and polymorphism."
Wofür Xin aber wiederum durchaus nützlich ist, ist für Lektürenreferenzen. Ich kann zwar bislang keiner seiner Textinterpretationen zustimmen, aber Interessantes und Neues gibt es dort immer zu finden. Und als Leseratte lehne ich nie Lektüretips nie ab.Vollkommen richtig. Und vermutlich meint er damit "plain classes", die er abseits dazu aufführt und vermutlich nimmt er grade deswegen ein Beispiel, dass die Verwendung von virtual aufzeigt, um OOP vorzuführen.
Ich weiß nicht, wie Du oder der Rest Texte interpretiert, aber derzeit hört sich das so an, als würde man interpretieren mit ignorieren verwechseln. ^^Stimmt, du ignorierst auch alle Sätze die Bjarne Stroustrup zwischen dem Kommentierten und den Beispiel schreibt, ebenso wie ein Teil des Kommentars als solchen (z.B. encapsulation, inheritance), wenn du OO nur und ausschließlich auf virtuell begründest.
In the context of C++ (and many other languages with their roots in Simula), it means programming using class hierarchies and virtual functions to allow manipulation of objects of a variety of types through well-defined interfaces and to allow a program to be extended incrementally through derivation...
Aber im Zweifel kann jeder den bereits geposteten Link folgen und seine eigenen Interpretationen von Bjarne Stroustrup's Ansichten machen (hier nochmal: http://www.research.att.com/~bs/bs_faq.html#oop).
Mir ist ehrlich gesagt egal wie du die Begriffe Interpretation und Ignorieren verwendest, für meine Interpretation versuche ich jedenfalls möglichst den Gesamtkontekt heranzuziehen und nicht alles das was mir nicht passt zu Ingorieren. Ich habe in meinen Leben mich schon ab und zu eines besseren belehren lassen, aber ich bezweifel das du andere Ansichten auch nur in Betracht ziehst. Mir fällt da nur "Was nicht passt wird passend gemacht" oder "Brecheisenmentalität" ein.
cu André
-
otze schrieb:
Xin schrieb:
Sobald man Raketen hinzufügt, muss collide_with verändert werden und collide-with kann auch nicht mehr auflösen.
Löst man das ganze mit OOP, also virtuellen Funktionen auf, ergibt sich an collide_with keine Änderung.Erkennst Du nun den Unterschied?
falsch. Wenn du vererbung verwendest, musst du jede Klasse verändern, wenn du Rakete hinzufügst.
Sorry, ich habe die Frage vorausgesehen und war grade damit beschäftigt, das Posting entsprechend zu erweitern.
otze schrieb:
Ohja, nicht zu vergessen, dass du dann noch das Problem hast, dass jede Klasse erstmal rauskriegen muss, welchen genauen typ nun das übergebene objekt hat, damit dann die endgültige methode aufgerufen werden kann.
Das Problem verstehe ich grade nicht - das ist in C++ schließlich kein Problem und es werden weniger Abfragen benötigt als mit Multimethoden, da hier nicht die Kombination aus 2 Parametern gesucht werden muss (n*n) Möglichkeiten, sondern der erste Parameter den zweiten sucht (n) Möglichkeiten und im Zweifelsfall die Suche mit dem zweiten Parameter wiederholt werden muss (n+n) Möglichkeiten.
Die Komplexität nimmt also ab. (Natürlich lässt sich an Multimethoden da auch was optimieren...)otze schrieb:
Aber wozu mach ich mir die Mühe? mir antwortest du ja eh net
Habe ich Dich bisher nicht ausreichend beachtet? Sorry.
asc schrieb:
Xin schrieb:
asc schrieb:
Nachtrag: Ein Hoch auf Bjarne Stroustrup, und seinen einfachen und kleinen Satz: "That said, object-oriented programming is a style of programming originating with Simula (about 40 years ago!) relying of encapsulation, inheritance, and polymorphism."
Wofür Xin aber wiederum durchaus nützlich ist, ist für Lektürenreferenzen. Ich kann zwar bislang keiner seiner Textinterpretationen zustimmen, aber Interessantes und Neues gibt es dort immer zu finden. Und als Leseratte lehne ich nie Lektüretips nie ab.Vollkommen richtig. Und vermutlich meint er damit "plain classes", die er abseits dazu aufführt und vermutlich nimmt er grade deswegen ein Beispiel, dass die Verwendung von virtual aufzeigt, um OOP vorzuführen.
Ich weiß nicht, wie Du oder der Rest Texte interpretiert, aber derzeit hört sich das so an, als würde man interpretieren mit ignorieren verwechseln. ^^Stimmt, du ignorierst auch alle Sätze die Bjarne Stroustrup zwischen dem Kommentierten und den Beispiel schreibt, ebenso wie ein Teil des Kommentars als solchen (z.B. encapsulation, inheritance), wenn du OO nur und ausschließlich auf virtuell begründest.
Lies doch mal die Postings...
Eine Klasse, die virtuelle Funktionen besitzt, aber keine Basisklasse, macht keinen Sinn, richtig?
Wenn ich also für virtual die Fahne schwenke, dann setzt das Vererbung voraus, den sonst gäbe es keine unterschiedlichen, aber verwandten Objekttypen, an denen man sich orientieren müsste. Man könnte direkt alles statisch machen und auf OOP verzichten.Auf die Notwendigkeit von Vererbung habe ich bereits mehrfach hingewiesen. ^^
Das die Daten klassifiziert und damit zusammengefasst werden müssen... muss darauf wirklich noch hingewiesen werden? Und wenn ja, auch das ist bereits geschehen.asc schrieb:
[...]für meine Interpretation versuche ich jedenfalls möglichst den Gesamtkontekt heranzuziehen und nicht alles das was mir nicht passt zu Ingorieren. Ich habe in meinen Leben mich schon ab und zu eines besseren belehren lassen, aber ich bezweifel das du andere Ansichten auch nur in Betracht ziehst. Mir fällt da nur "Was nicht passt wird passend gemacht" oder "Brecheisenmentalität" ein.
Mir fällt nur auf, dass Du Teile meiner Postings ignorierst, sonst wäre die vorherige Frage nicht möglich gewesen.
Wenn Du Dich also in Deinem Leben belehren lassen möchtest, dann solltest Du weniger ignorieren.
-
Xin schrieb:
Programmierung bedeutet Algorithmen zu formulieren. Man programmiert, damit sich Algorithmen um Daten (==Objekte) kümmern.
Ok, wir merken uns: Objekte fallen vom Himmel, denn Klassen werden nicht programmiert.
Xin schrieb:
Würde man OOP daran ausrichten, dass sich der Programmierer an Objekten orientiert, ist absolut alles objektorientiert, was mit Objekten ("irgendein Ding", z.B. char, int, double..., aber eben nicht void) arbeitet.
Seit wann sind Zahlen Objekte? Ganz primitiv und einfältig: Katzen habe ich schonmal angefaßt, Zahlen nicht. Wenn die Sonne nicht so weit weg und so warm wäre, könnte ich die auch anfassen.
Ich behaupte mal, ohne es zu beweisen: Zahlen kannst Du nicht anfassen- was ein Widerspruch zur Aussage steht, sie seien Objekte.Xin schrieb:
Nach eurer Definition sind Arrays eine objektorientierte Technik, weil der Programmierer sich an Objekten orientiert, wenn der Arrays erzeugt, die Objekte speichern. Die Definition von OOP macht doch keinen Sinn, wenn sie so schwammig ist, dass man alles in sie hineininterpretieren kann.
Deine Interpretation ist einfach mutwillig falsch. Von objektorientierter Technik hat niemand geredet. Und außerdem ist es natürlich im Sinne "meiner" Definition objektorientiere Programmierung, einen Array von Objekten anzulegen- aber das hängt nicht am Array.
-
Xin schrieb:
.filmor schrieb:
Jester schrieb:
Ich bestreite den Faktor 5-10. Her mit den Sourcen, dann schaun wir weiter.
Treffer.
hashmap (in C) vs. map (in C++) ? Das dürfte wohl kaum als äquivalent gelten... bei hinreichender Skrupellosigkeit ist sowieso alles beweisbar.
-
Xin schrieb:
Korrekt. Aber das ist doch nur bedingt objektorientiert, da die möglichen Objekttypen zur Compilezeit bekannt sein müsen.
Diese Technik hat man mit virtuellen Funktionen (Edit: "OOP" entfernt) ja eben entfernen wollen, schließlich wurden so in C die Fehler produziert.Helium schrieb:
(defmethod collide-with ((x asteroid) (y asteroid)) ;; deal with asteroid hitting asteroid ) (defmethod collide-with ((x asteroid) (y spaceship)) ;; deal with asteroid hitting spaceship ) (defmethod collide-with ((x spaceship) (y asteroid)) ;; deal with spaceship hitting asteroid ) (defmethod collide-with ((x spaceship) (y spaceship)) ;; deal with spaceship hitting spaceship )
entspricht:
void collide_with( SpielObjekt & lhs, SpielObjekt & rhs ) { if( typeid( lhs ) == typeid( asteroid ) && typeid( rhs ) == typeid( asteroid ) ;; deal with asteroid hitting asteroid if( typeid( lhs ) == typeid( asteroid ) && typeid( rhs ) == typeid( spaceship ) ;; deal with asteroid hitting spaceship if( typeid( lhs ) == typeid( spaceship ) && typeid( rhs ) == typeid( asteroid ) ;; deal with spaceship hitting asteroid if( typeid( lhs ) == typeid( spaceship ) && typeid( rhs ) == typeid( spaceship ) ;; deal with spaceship hitting spaceship }
Sobald man Raketen hinzufügt, muss collide_with verändert werden und collide-with kann auch nicht mehr auflösen.
Löst man das ganze mit OOP, also virtuellen Funktionen auf, ergibt sich an collide_with keine Änderung.Erkennst Du nun den Unterschied?
Nein. Deine "Übersetzung" enthält einen IMO groben Fehler: Deinen Dispatcher kann bzw. muss ich an einer Stelle erweitern. Das ganze ist folglich umständlich um neue Typen zu erweitern, da vorhandener Code geändert werden muss. Bei Multimethoden kann ich die Methoden da definieren, wo ich will, also auch da, wo ich die neue Rakete hinzufüge.
Das heißt ich muss beim Hinzufügen von neuen Typen nichts an alten Teilen ändern, sondern tatsächlich nur hinzufügen.
-
otze schrieb:
Ohja, nicht zu vergessen, dass du dann noch das Problem hast, dass jede Klasse erstmal rauskriegen muss, welchen genauen typ nun das übergebene objekt hat, damit dann die endgültige methode aufgerufen werden kann.
Das Problem verstehe ich grade nicht - das ist in C++ schließlich kein Problem und es werden weniger Abfragen benötigt als mit Multimethoden, da hier nicht die Kombination aus 2 Parametern gesucht werden muss (n*n) Möglichkeiten, sondern der erste Parameter den zweiten sucht (n) Möglichkeiten und im Zweifelsfall die Suche mit dem zweiten Parameter wiederholt werden muss (n+n) Möglichkeiten.
Die Komplexität nimmt also ab. (Natürlich lässt sich an Multimethoden da auch was optimieren...)Es sind auch bei Multimethods immer n+n möglichkeiten:
void multimethod(const Foo* a,const Foo* b) { if(typeid(a)==typeid(Bar)) { if(typeid(b)==typeid(Bar) { callBarBar(a,b) } if(typeid(b)==typeid(FuBa) { callBarFuBa(a,b) } ... } if(typeid(a)==typeid(FuBa)) { if(typeid(b)==typeid(Bar) { callFuBaBar(a,b) } if(typeid(b)==typeid(FuBa) { callFuBaFuBa(a,b) } ... } ... };
-
camper schrieb:
Xin schrieb:
.filmor schrieb:
Jester schrieb:
Ich bestreite den Faktor 5-10. Her mit den Sourcen, dann schaun wir weiter.
Treffer.
hashmap (in C) vs. map (in C++) ? Das dürfte wohl kaum als äquivalent gelten... bei hinreichender Skrupellosigkeit ist sowieso alles beweisbar.
C-Funktionalität gegen C++-Funktionalität.
Was wird der Otto-Normal-Programmierer verwenden?
Wie ist die Performance, wenn man C statt C++ nimmt und normal programmiert?Man kann auch von beiden Sprachen eine externe Lib aufrufen, die das Problem optimal löst. Das wäre äquivalent, aber langweilig.
-
Xin schrieb:
C-Funktionalität gegen C++-Funktionalität.
Was wird der Otto-Normal-Programmierer verwenden?
Wie ist die Performance, wenn man C statt C++ nimmt und normal programmiert?die äquivalente C funktionalität wäre aber ein selbst geschriebener RB Baum, ansonsten musst du den test halt mit deinen std::tr1::hash_maps wiederholen.
Und ein ottonormalnutzer wird sicher keine hashmap basteln, weil die auch ein *wenig* mathematisches verständnis erfordert.
-
lol, das Problem ist, dass dadurch Datenstrukturen verglichen werden und nicht die Sprache. Wenn man Mikrobench durchführt, müssen die Algo und Datenstrukturen ähnlich sein.
-
Xin schrieb:
C-Funktionalität gegen C++-Funktionalität.
Was wird der Otto-Normal-Programmierer verwenden?den Vergleich kannst du knicken. C bietet dir auch keine Hashmap in der Standard Library. Also müsste nach deiner Aussage der Otto-Normal-Programmierer in C wohl gar nichts verwenden.
Ansonsten gibt es unsorted_map in C++...
Wie ist die Performance, wenn man C statt C++ nimmt und normal programmiert?
Dafür gibt es die "Abstraction Penalty Benchmarks" von Stepanov und nach dem was Stroustrup im HOPL-III schreibt, ist bei aktuellen Implementierungen der Faktor bei 1.02
Man kann auch von beiden Sprachen eine externe Lib aufrufen, die das Problem optimal löst. Das wäre äquivalent, aber langweilig.
Ne, man kann auch den ganzen Tag rumsitzen und sich schlechte Benchmarks überlegen um jeden Standpunkt zu beweisen...
-
Man kann auch von beiden Sprachen eine externe Lib aufrufen, die das Problem optimal löst. Das wäre äquivalent, aber langweilig.
Geht es hier um Hobby oder um professionelle Arbeit?
-
Helium schrieb:
Nein. Deine "Übersetzung" enthält einen IMO groben Fehler: Deinen Dispatcher kann bzw. muss ich an einer Stelle erweitern. Das ganze ist folglich umständlich um neue Typen zu erweitern, da vorhandener Code geändert werden muss. Bei Multimethoden kann ich die Methoden da definieren, wo ich will, also auch da, wo ich die neue Rakete hinzufüge.
Das heißt ich muss beim Hinzufügen von neuen Typen nichts an alten Teilen ändern, sondern tatsächlich nur hinzufügen.Das hilft aber nicht dabei, dass wenn man vergißt die Multimethoden zu erweitern, dass es dann trotzdem knallt.
Und ob man nun vergißt die Multimethoden zu erweitern oder eine Funktion zu erweitern, es knallt.Es knallt auch, wenn man vergißt Rakete beizubringen, dass sie mit alten Objekten kollidieren kann. Richtig programmieren muss man so oder so, wenn man etwas hinzufügt muss man zwangsläufig irgendwo sinnvoll erweitern.
Der Unterschied ist, dass man nur die Klasse beschreiben muss und nicht über Dinge außerhalb der Klasse nachdenken muss und Implementierung durch Pure Virtual Funktionen voraussetzen kann, um eine gültige Implementierung vorauszusetzen.
Danke für das IMO... das klingt anders als "Das ist Unfug".
Nochmals der explizit Hinweis: Ich widerspreche nicht, dass Multimethoden eine Möglichkeit zur OOP-Programmierung darstellt. Ich sehe nicht, dass sie mächtiger wären als virtuelle Funktionen - das wird das C++ Kommitee davon abhalten, sie in C++ aufzunehmen. Das Kommitee ist ja recht konservativ und zurückhaltend, was das angeht.
Da Klassen mit pure virtual Functions die Implementierung an klar definierter Stelle verlangen können, sehe ich virtuelle Funktionen hier im Vorteil.Ich weiß auch nicht, wieso Multimethoden hier meiner Definition von OOP widersprechen würden.
otze schrieb:
dies gilt aber nur, solange die funktion kommutativ ist. In seinem buch "Modernes C++ Design" hat Alexandrescou dazu ein gutes beispiel gebracht bei dem die zu dispatchende funktion eben nicht kommutativ ist. Es sind also immer n+n möglichkeiten, und auf genau diesen Wert kommen multimethods auch.
Kommutativität ist natürlich bei dem obigen Beispiel vorausgesetzt. Bei Operatoren, die nicht kommutativ sind, kann man aber auch mal fragen...!? Ich gebe nicht kommutativen Operatoren ein Flag mit, einfach ein Bool, ob der Aufruf der erste ist (also lhs lhs ist) oder ich mich in einem Review befinde (also lhs ursprünglich rhs war).
Die Komplexität ist im Worst-Case also ((n-1) + (n-1) + 1) und der Worst-Case ist nicht der Regelfall.Wir können hier jetzt jedes Detail des Beispiels durchgehen, aber was widerspricht hier eigentlich meiner OOP-Definition?
-
Was soll das für ein Vergleich sein? Auf jeden Fall keiner OOP gegen nicht OOP, sondern eher spezielle Lösung gegen allgemeine.
In der C Lösung nimmt er ein Array als Hashtabelle und die hash Funktion gibt genau für die Arraygröße passende Werte zurück. Die Javalösung verwendet ne Hashtabelle mit beliebiger Größe und die hash Funktion gibt Werte im int Bereich zurück.
-
Nochmals der explizit Hinweis: Ich widerspreche nicht, dass Multimethoden eine Möglichkeit zur OOP-Programmierung darstellt. Ich sehe nicht, dass sie mächtiger wären als virtuelle Funktionen - das wird das C++ Kommitee davon abhalten, sie in C++ aufzunehmen. Das Kommitee ist ja recht konservativ und zurückhaltend, was das angeht.
Da Klassen mit pure virtual Functions die Implementierung an klar definierter Stelle verlangen können, sehe ich virtuelle Funktionen hier im Vorteil.Alexandrescou hat darüber einiges geschrieben. Für Codewartbarkeit ist es wichtig, dass man nur an sowenig Stellen wie möglich Code ändern muss.
Bei der double dispatch methode wären das der dispatcher selbst(mit templates sehr schnell erledigt, ohne templates etwas haariger) und die funktionen die durch ihn aufgerufen werden. dazu kommt dann die neue Klasse und das wars.
Mit deiner methode würde folgendes passieren: Die neue Klasse muss eine singleDispatch funktion erhalten(+die jeweiligen teilfunktionen), dann muss in jeder anderen Klasse noch dieser neue zweig zusätzlich eingebaut werden, und schlussendlich braucht jede Klasse noch die vorwärtsdeklaration der neuen Klasse. das ist ein riesiger aufwand, vorallem wenns sich nichtmehr um 3 klassen sondern um 10,15 oder mehr handelt. Mal davon ab, dass es enorm fehleranfällig ist. vergess ich in einer Klasse das erweitern, knallt es noch lange nicht, erst wenn ich versuche das Objekt der neuen klasse mit dieser Klasse zu nutzen, und das kann dauern.
Wenn ich den multidispatcher nicht erweiter, dann krieg ich sofort ne exception um die Ohren geworfen, weil er diesen neuen Typ nicht kennt. Und wenn ich die neue funktion vergesse, krieg ich nen compilezeit error, weil der multidispatcher versucht eine funktion aufzurufen, die es nicht gibt.
Der grund, weshalb multimethoden wahrscheinlich nicht in den Standardkommen ist desweiteren nicht, das virtual mächtiger ist, sondern hat den einfachen grund, dass man multidispatching nur sehr selten braucht, im gegensazu zu vector, set,map,fstream,function, bind... .
Ausserdem hat ein multidispatcher als implementation in der std lib die anforderung, dass es eine typelist oder ähnliches gibt, und da variadic templates erst mit dem nächsten standard kommen, hat ein multidispatcher davor keinen Sinn.
-
rüdiger schrieb:
Dafür gibt es die "Abstraction Penalty Benchmarks" von Stepanov und nach dem was Stroustrup im HOPL-III schreibt, ist bei aktuellen Implementierungen der Faktor bei 1.02
Ist doch super!
Mit 1.02 bewegt man sich - imho - aber auch am Maximum. Es wird immer einen Faktor geben und was anderes habe ich nicht behauptet. Faktor 5-10 habe ich aus dem Buch übernommen und ich habe es oft genug woanders gelesen. Wenn der Benchmark euch nicht gefällt und ihr einen habt, der besser läuft, ist das toll. Der Faktor bleibt aber trotzdem.Aus dem Posting, wo ich den Faktor erstmals nannte (Seite 8):
Xin schrieb:
Die Zahlen sind alt, die Differenz sollte inzwischen kleiner sein, aber selbst würde ein Programm 10x langsamer laufen, hilft C++ deutlich, Programme übersichtlicher und damit fehlerfreier zu gestalten. Ein Programm, dass nach 10 Stunden zum Ziel kommt ist effizienter als ein Programm, das nach 1 Stunde ein falsches Ergebnis liefert.
Da meine Betrachtung von C++ nicht so einseitig ist, dass es 5-10 mal langsamer sein muss, habt ihr jetzt etwas belegt, was ich sofort mit Angabe des Faktors auf Seite 8 einräumte: Die Zahlen sind alt, die Differenz ist kleiner und ein Faktor größer 1 existiert trotzdem.
Ungeachtet davon:
Artchi schrieb:
Man kann auch von beiden Sprachen eine externe Lib aufrufen, die das Problem optimal löst. Das wäre äquivalent, aber langweilig.
Geht es hier um Hobby oder um professionelle Arbeit?
Gute Frage, sind Kerningham und Pike (welcher immerhin an der Entwicklung von zwei OS beteiligt war), Hobby- oder professionell anerkannte Entwickler?
Wenn beide den Vergleich machen und die Codes nicht optimal hinbekommen, dann frage ich mich, ob es eine Rolle spielt, ob es sich um hobby- oder professioneller Entwickler besser machen kann. Wenn Kerningham und Pike hier schon nicht optimale Sourcen verwenden, dann weiß ich nicht, ob ein normaler Professioneller, der grade 70 Zeilen Code nicht auf Optimierungsmöglichkeiten durchsucht, es besser machen würde.
Und wenn er es nicht besser macht, weil er auf andere Dinge wert legt (und das sind nicht wenige), dann haben wir sofort wieder einen Faktor von 5-10.Der Part der Diskussion ging um C++ und war - wie man ebenfalls auf Seite 8 nachlesen kann - nicht auf OOP gemünzt. Nur für den Fall, dass jemand glaubt, dass gut geschriebene C++-Programme belegen im Widerspruch zu meiner OOP-Definition stehen würden.
-
Du schriebst eben, ob Sinn macht zu antworten, ich antworte Dir nicht. Darum antworte ich Dir hier. Sinn hat es eigentlich nicht, weil auch hier alles schon genannt wurde.
otze schrieb:
Alexandrescou hat darüber einiges geschrieben. Für Codewartbarkeit ist es wichtig, dass man nur an sowenig Stellen wie möglich Code ändern muss.
Dem stimme ich zu.
otze schrieb:
Bei der double dispatch methode wären das der dispatcher selbst(mit templates sehr schnell erledigt, ohne templates etwas haariger) und die funktionen die durch ihn aufgerufen werden. dazu kommt dann die neue Klasse und das wars.
Schön, ob Du es jetzt ausschreibst oder einen Teil davon durch ein Template erzeugen läßt, Du darfst es nicht vergessen, sonst bäng!
Templates helfen nicht dabei, Dinge nicht zu vergessen.otze schrieb:
Mit deiner methode würde folgendes passieren: Die neue Klasse muss eine singleDispatch funktion erhalten(+die jeweiligen teilfunktionen), dann muss in jeder anderen Klasse noch dieser neue zweig zusätzlich eingebaut werden,
Posting lesen, ich habe extra drauf hingewiesen, dass ich diesbezüglich erweitert habe, damit man nicht selbst denken muss und auch die Frage nach kommutativen Operatoren ist geklärt.
Nochmal zum mitmeißeln: Es muss nicht jede Klasse erweitert werden.otze schrieb:
Wenn ich den multidispatcher nicht erweiter, dann krieg ich sofort ne exception um die Ohren geworfen, weil er diesen neuen Typ nicht kennt.
Super. Den Anwender wird's freuen.
Eine pure virtual Function lässt den Compiler meckern.otze schrieb:
Der grund, weshalb multimethoden wahrscheinlich nicht in den Standardkommen ist desweiteren nicht, das virtual mächtiger ist
Ich sagte nicht, dass es mächtiger ist, ich sagte, dass sie dem Compiler die Möglichkeit geben, zu meckern.
-
Xin schrieb:
...Wenn Kerningham und Pike hier schon nicht optimale Sourcen verwenden, dann weiß ich nicht, ob ein normaler Professioneller, der grade 70 Zeilen Code nicht auf Optimierungsmöglichkeiten durchsucht, es besser machen würde.
Und wenn er es nicht besser macht, weil er auf andere Dinge wert legt (und das sind nicht wenige), dann haben wir sofort wieder einen Faktor von 5-10.Der Part der Diskussion ging um C++ und war - wie man ebenfalls auf Seite 8 nachlesen kann - nicht auf OOP gemünzt. Nur für den Fall, dass jemand glaubt, dass gut geschriebene C++-Programme belegen im Widerspruch zu meiner OOP-Definition stehen würden.
Wenn ich mir einige C Entwickler anschaue und das was sie verbrocken könnte es auch genau andersrum (Faktor 5-10 für C++) sein. Aber Xin, du kannst gerne so weiter diskutieren, das hat etwa den Nutzen wenn man gegen eine Felswand spricht.
Wenn man Vergleiche zieht muss man auf beiden Seiten auch Gleichwertiges gegenüberstellen.
Ist aber egal, den egal was einer sagt wirst du es dir eh so hinbiegen wie du willst, selbst wenn es der ursprünglichen Aussage zuwieder läuft.
cu André
-
asc schrieb:
Xin schrieb:
...Wenn Kerningham und Pike hier schon nicht optimale Sourcen verwenden, dann weiß ich nicht, ob ein normaler Professioneller, der grade 70 Zeilen Code nicht auf Optimierungsmöglichkeiten durchsucht, es besser machen würde.
Und wenn er es nicht besser macht, weil er auf andere Dinge wert legt (und das sind nicht wenige), dann haben wir sofort wieder einen Faktor von 5-10.Der Part der Diskussion ging um C++ und war - wie man ebenfalls auf Seite 8 nachlesen kann - nicht auf OOP gemünzt. Nur für den Fall, dass jemand glaubt, dass gut geschriebene C++-Programme belegen im Widerspruch zu meiner OOP-Definition stehen würden.
Wenn ich mir einige C Entwickler anschaue und das was sie verbrocken könnte es auch genau andersrum (Faktor 5-10 für C++) sein. Aber Xin, du kannst gerne so weiter diskutieren, das hat etwa den Nutzen wenn man gegen eine Felswand spricht.
Eigentlich habe ich in dem Posting auf Seite 8 so ziemlich alles gesagt - es müsste halt nur mal einer bewußt lesen:
Xin schrieb:
Nutzt man OOP bei Funktionsrufen, wo keine Entscheidungen zu treffen sind, so verliert man Zeit. Nutzt man OOP bei Funktionen, die objektabhängig sind, spart man Zeit oder viel Aufwand und erhält übersichtlichen Code. Man verliert in dem Fall aber keine Zeit, weil die Entscheidung muss so oder so getroffen werden. Und in der Regel wird sie schlechter implementiert, als es C++ macht. In dem Fall ist OOP mit C++ schneller.
Man muss OOP sinnvoll und bewußt einsetzen.