Performancemythen?
-
Dieser Thread hat Kult-Potenzial.
-
Shade Of Mine schrieb:
Xin schrieb:
In allen Fällen wird abhängig vom Objekttyp gehandelt. Das alles ist OOP. Und das ist auch die Definition von OOP:
"OOP liegt vor, wenn Objekte sich abhängig von ihrem Objekttyp verhalten und nicht abhängig vom ReferenzTyp, der auf das Objekt zeigt.":Und warum genau ist dann die Definition
"Objekt orientierung bedeutet sich an den Objekten zu orientieren [was das verhalten des Programmes betrifft]" falsch?Es geht um die Algorithmen, die abhängig vom Objekttyp zur Laufzeit unterschiedliche Funktionen aufrufen.
Ich weiß jetzt nicht, in welchem Zusammenhang Du den Satz grade zitierst oder schreibst. Solange sich nur Programmierer an Objekten orientieren, nicht aber die Algorithmen, ist er falsch.
Wenn Du statt "Objekten" "Objekttypen" meinst, wäre ich für die Ablehnung von Referenztypen dankbar, denn dann wären solche Threads überflüssig.Shade Of Mine schrieb:
Nichts anderes als das sagen wir hier die ganze Zeit: OOP ist eine Denkweise in der das Objekt das Zentrum dastellt. Ich brauche dafür aber keine dynamische Polymorphie - ich kann das auch mit statischer machen - solange das Objekt das Zentrum ist, das Objekt das verhalten festlegt.
Du brauchst keine dynamische Polymorphie, das habe ich nie behauptet.
Ich sagte, dass "virtual" dynamische Polymorphie benötigt.
Und dynamische Polymorphie braucht zwangsläufig Vererbung.
Vererbung geht nur mit Klassifizierung, in C durch Klassen.
Schlussfolgerung: für OOP-Unterstützung von C++ benötigst Du Klassifizierung, Vererbung, dynamische Polymorphie (also virtual).Und ich sagte mehrfach, dass virtual nicht für OOP benötigt wird. Verzichtest Du auf virtual, wirst Du von C++ auch nicht unterstützt und musst Du Deine Objektorientierung selbst implementieren. Also statische Jumptables aufwendig nachprogrammieren oder die in C häufig (fehleranfällige) verwendete Möglichkeit nehmen:
void oop_function( char * object ) { if ( object[0] == 1 ) printf( "%s\n", &object[1] ); else if( object[0] == 2 ) printf( "%c\n", object[1] ); else if( object[0] == 3 ) printf( "%d\n", object[1] ); ... }
Das ist schwer zu warten, sieht scheiße aus und ist langsam. Es ist aber trotzdem OOP, weil abhängig vom Objekt (das auch keine "class" sein muss) unterschiedliche Funktionen gerufen werden.
Das ist etwa das Level von Multifunktionen, lediglich ist ausformuliert, was ein Compiler übersetzen würde.
Und es ist verdammt gut, dass diese ausgeschriebene Form von OOP heutzutage verpöhnt ist - es ändert aber nichts daran, dass es eine Form von objektorientierte Programmierung ist.In C++ würde ich die Objekte klassifizieren und die Sache über virtual functions regeln, weil C++ OOP unterstützt, damit lässt es sich besser warten, sieht es nicht mehr scheiße aus und wird schneller. (alles schon geschrieben...)
Das x-Mal gepostete Tier => Katze Beispiel, ist ohne virtual nicht OOP, weil egal ist, mit welchem Objekt Tier::GibLaut() gerufen wird, es kommt Hallo raus.
-
otze schrieb:
Xin schrieb:
"OOP liegt vor, wenn Objekte sich abhängig von ihrem Objekttyp verhalten und nicht abhängig vom ReferenzTyp, der auf das Objekt zeigt.":
Das ist aber unlogisch. Ein Objekt muss sich immer so verhalten, wie der Referenztyp der auf es zeigt, denn der Referenztyp gibt die definition darüber, was das Objekt ist.
Falsch.
Ein Referenztyp gibt an, was das Objekt ist oder, zu welcher Oberklasse er zugeordnet wird.BITTE schau Dir das Tier <-> Katze Beispiel an, kompilier es.
Das Objekt ist eine Katze, aber handelt nicht wie eine Katze. Es ist nicht Objektorientiert, weil nach dem Referenztyp (Tier) entschieden wird Tier::GibLaut() aufzurufen, statt Katze::GibLaut().
otze schrieb:
Du kannst das Objekt nicht anders verwenden als der Referenztyp zulässt, und der Objekttyp darf sich niemals anders verhalten als das was die zusicherungen erlauben.
Doch kannst Du, denn genau das ist, was Objektorientierung ausmacht.
-
Xin schrieb:
Doch kannst Du, denn genau das ist, was Objektorientierung ausmacht.
Also wirklich, billiger geht es nicht: Gerade klärst Du ihn erst darüber auf, was ein Referenztyp wirklich ist, und jetzt trittst Du hier noch nach.
Xin schrieb:
Das x-Mal gepostete Tier => Katze Beispiel, ist ohne virtual nicht OOP, weil egal ist, mit welchem Objekt Tier::GibLaut() gerufen wird, es kommt Hallo raus.
Im Bereich der minimalen Voraussetzungen "meiner" Definition kommt man aber gar nicht in die Verlegenheit, Klassen abzuleiten.
Natürlich ist Dein Beispiel eins objektorientierter Programmierung- aber nicht wegen der Verwendung von virtual, sondern weil Du Dich entschlossen hast, statt wild verstreuter Daten und Funktionen eine Klasse zu programmieren (Verzeihung, "zu schreiben", eine Klasse ist ja kein Algorithmus) und sie dazu zu benutzen, ein Programm zu formulieren, das mit Objekten arbeitet, die die so definierten Eigenschaften haben. Ich sehe nicht, was daran nun schwammig ist (wer soll das übrigens behauptet haben?).
Xin schrieb:
Es ist aber trotzdem OOP, weil abhängig vom Objekt (das auch keine "class" sein muss) unterschiedliche Funktionen gerufen werden.
Ich merke mir als intellektuelle Höhepunkte von und für heute:
1. Klassen müssen keine Objekte sein (logisch, den U-Boote tanzen nicht)
2. Der folgende Codeschnipsel ist OOP, weil abhängig vom Objekt verschiedene Funktionen gerufen werden:class Katze { public: void GibLaut() { std::cout << "Miau"; } }; class Hund { public: void GibLaut() { std::cout << "Wau"; } }; Katze k; Hund h; h.GibLaut(); k.Giblaut();
Zu einer anderen Frage von Dir: Als geübter Definierer sollte schon klar sein, daß Einzelteile einer Definition ggf. auch definiert sein müssen. Wenn Du OOP definierst, mußt Du also automatisch auch O, O und P definieren. Und im Prinzip können wir jetzt schon aufhören, weil Du die Definition von O dazu genutzt hast, "meine" Definition für viel zu allgemein zu erklären. "mutwillig falsche Interpretation".
-
Xin schrieb:
Es geht um die Algorithmen, die abhängig vom Objekttyp zur Laufzeit unterschiedliche Funktionen aufrufen.
Ich weiß jetzt nicht, in welchem Zusammenhang Du den Satz grade zitierst oder schreibst. Solange sich nur Programmierer an Objekten orientieren, nicht aber die Algorithmen, ist er falsch.Dass man da so einfach trennen kann... Schliesslich schreibt der Programmierer ja den Algorithmus und ich kann mir schwer vorstellen dass der Programmierer das Verhalten vom Objekt vorgeben lässt dies aber nicht in den Algorithmus einbaut. Irgendwie widerspricht sich da etwas.
Wenn Du statt "Objekten" "Objekttypen" meinst, wäre ich für die Ablehnung von Referenztypen dankbar, denn dann wären solche Threads überflüssig.
Typen sind ein Konzept dass einige Sprachen nicht wirklich haben.
Du brauchst keine dynamische Polymorphie, das habe ich nie behauptet.
Ich sagte, dass "virtual" dynamische Polymorphie benötigt.
Und dynamische Polymorphie braucht zwangsläufig Vererbung.
Vererbung geht nur mit Klassifizierung, in C durch Klassen.
Schlussfolgerung: für OOP-Unterstützung von C++ benötigst Du Klassifizierung, Vererbung, dynamische Polymorphie (also virtual).Aber das alles ist komplett uninteressant. Wir alle wissen welche Features C++ bietet. Die Frage ist: definieren diese Feature OOP. Lass C++ und Java einfach mal ruhen. Nur weil virtual in C++ sinnvoll ist um gute dynamische Polymorphie zu erreichen bedeutet dass nicht, dass virtual OOP definiert. Und es geht hier um OOP und nicht um C++.
Ich behaupte weiterhin: diese Features sind Ausdrucksformen - ein paar von vielen Möglichen.
Und ich sagte mehrfach, dass virtual nicht für OOP benötigt wird.
Du sagst aber dynamische Polymorphie ist nötig für OOP und statische Polymorphie ist kein Teil der OOP. Genau das hinterfrage ich hier.
Das x-Mal gepostete Tier => Katze Beispiel, ist ohne virtual nicht OOP, weil egal ist, mit welchem Objekt Tier::GibLaut() gerufen wird, es kommt Hallo raus.
Und genau das ist nicht wahr:
class Hund { public: void gib_laut() { cout<<"wau wau"; } }; class Katze { public: void gib_laut() { cout<<"miau"; } }; template<typename Tier> void foo(Tier* t) { t->gib_laut(); }
voila - das Objekt bestimmt das Verhalten.
Die Frage ist: warum lässt du das nicht als OOP gelten? Vermutlich weil es statisch ist und schon zur Compiletime feststeht. Nun, was wenn ich einen C++ Interpreter hernehme - dann steht alles erst zur Laufzeit fest.
-
Xin schrieb:
Falsch.
Ein Referenztyp gibt an, was das Objekt ist oder, zu welcher Oberklasse er zugeordnet wird.BITTE schau Dir das Tier <-> Katze Beispiel an, kompilier es.
das ohne virtual? ich kenne das ergebnis. und nun? Das ist auch das, was mir das interface zusichert. Nichts magisches, nichts ungewöhnliches. Es gibt einen laut zurück, und den habe ich verlangt. Das Objekt hat das Interface eingehalten. Und gleichzeitig auch nicht, aufgrund des logischen Fehlers. Das passiert aber oft, nennt man Bug. damit mein ich nicht, dass kein "Miau" ausgegeben wird, sondern "Hallo". Als Mensch erwarte ich nicht unbedingt, dass ein tier reden kann, ausser natürlich ich bin der Meinung, dass der Mensch biologisch ein Tier ist, für sicher 60% der menschen ist das aber höchst befremdlich.
Das Objekt ist eine Katze, aber handelt nicht wie eine Katze. Es ist nicht Objektorientiert, weil nach dem Referenztyp (Tier) entschieden wird Tier::GibLaut() aufzurufen, statt Katze::GibLaut().
Das ist aber nur logisch falsch, denn der Referenztyp kann diese Aufgabe ebenso erfüllen.
class Tier { private: string laut; public: Tier():laut("Hallo"){} Tier(const string& laut):laut(laut){} void gibLaut(){std::cout<<laut<<"\n";} }; class Katze: public Tier { public: Katze():Tier("Miau"){} }; int main() { Tier* tier = new Katze(); tier->GibLaut(); }
Das Ergebnis gibt das was wir erwarten. Das objekt verhält sich dem Vertrag entsprechend und gibt uns den geforderten laut zurück. logisch und semantisch korrekt
Ist das nun OOP?mal davon ab: wenn wir das Objekt nicht selbst erstellen würden, könnten wir sogar(nach der philosophischen diskussion ob der Mensch auch ein Tier ist) zum Schluss kommen, dass "Hallo" durchaus korrekt ist, was meine Meinung verstärkt, dass der Vertrag das wichtige an der OOP ist, kein virtual.
-
Vielleicht erstmal Begrifsklärung Polymorphie:
Beispiel Coercion-Polymorphie:
void foo (double x) {} double bar = 0.0; foo (bar); int baz = 0; foo (baz);
foo lässt sich mit Objekten verschiedenen Typs aufrufen.
Überladungs-Polymorphie:
void foo (double x) {} void foo (int x) {} double bar = 0.0; foo (bar); int baz = 0; foo (baz);
foo lässt sich mit Objekten verschiedenen Typs aufrufen.
Parametrische Polymorphie:
template<typename T> void foo (T x) {} double bar = 0.0; foo (bar); int baz = 0; foo (baz);
foo lässt sich mit Objekten verschiedenen Typs aufrufen.
...
Einzig bei der Inklusions-Polymorphie wird dann dynamisch entschieden.
struct X { virtual void foo() {} }; struct Y : X { virtual void foo() {} }; X x; Y y; X * bar = x; bar->foo(); // ist jetzt leider die C++-Syntax. Cooler wäre eine Sprache, die die Syntax foo(bar) verwenden würde bar = y; bar->foo();
-
Erkenntnisgewinnbringende Diskussion schrieb:
<ZENSIERT-1> weißte warum Pferde Pferde heißen?
<ZENSIERT-2> pferde sind nicht objektorientiert.
<ZENSIERT-1> weil sie auf der erde stehen, wären sie in der luft hießen sie Pflufte!
<ZENSIERT-3> pferde sind grasorientiert.
<ZENSIERT-1> völlig klar!
<ZENSIERT-1> ich habe jetzt ausführlich erklärt warum Pferde Pferde heißen!
<ZENSIERT-4> ZENSIERT-1: aber sehr schwammig
<ZENSIERT-5> sehr sehr schwammig
<ZENSIERT-4> die Pferde orientieren sich doch gar nicht an der Erde!
<ZENSIERT-5> das ist also dein verstaendnis von mathematik
<ZENSIERT-6> ZENSIERT-1: Nein, Pferde heissen nur so weil sie virtuelle Methoden haben. Sonst wären es ja Pferdbirnen
<ZENSIERT-5> schaem dich ZENSIERT-1
<ZENSIERT-1> ZENSIERT-4: ich verlasse mich da auf das Fundament der Logik, wenn ihr mir nicht glaub könnt ihr auch gleich Bildzeitung lesen!
-
@Helium: Ist das ein scherz-Posting?
Helium schrieb:
Beispiel Coercion-Polymorphie:
Das ist keine Polymorphie, es wird nur gecastet (implizit). Ist das gleiche, wie wenn ich fuer alle Typen eine eigene Funktion schreibe. Gut, dass das der Compiler fuer mich uebernimmt.
Helium schrieb:
Überladungs-Polymorphie:
WTF, siehe oben.
Helium schrieb:
Parametrische Polymorphie:
Das ist das gleiche wie die beiden oberen Beispiele. Der Compiler uebernimmt fuer mich die Drecksarbeit.
Helium schrieb:
Einzig bei der Inklusions-Polymorphie wird dann dynamisch entschieden.
Und das ist OOP.
Shade Of Mine schrieb:
klingt sehr vereinfacht. warum muss alles schwarz oder weiß sein? kann nicht etwas grau sein?
Sind wir in der Politik? Bundertrojaner ist gut und nur ein wenig schlecht, sieht das doch mal (hell-)grau und nicht so schwarz.
Shade Of Mine schrieb:
Warum?
Ich rede nicht von austauschen zur Laufzeit. Ich rede einfach nur von Laden von Klassen zur Laufzeit. Warum ist es Polymorphie wenn ich die Klassen die es gibt doch eh statisch vor mir habe? Wo trennst du ab wann etwas zu statisch für Polymorphie wird.Wenn man nur mit dem Interface arbeitet und Instanzen fuer dieses Interface dynamisch mittels Reflection nachlaed, dann sehe ich das als OOP. Man koennte es auch mittels C++ Interpreter machen, der neue Templates erzeugt. Aber nur Templates, einmal kompiliert und danach nie wieder, ist keine OOP.
Shade Of Mine schrieb:
call mal bitte basisklasse::mach_was aus der "abgeleiteten" klasse
Das hat mich auch gewundert. Wie macht man das in JS?
Also als Beispiel:
Ich habe ein array, der Zahlen aufnehmen kann. Wie erweitere ich jetzt die .add() Methode, ohne die Methode gleich neu schreiben zu muessen? Das weis ich bis Heute nicht, wie das in JS geht.Shade Of Mine schrieb:
"Objekt orientierung bedeutet sich an den Objekten zu orientieren [was das verhalten des Programmes betrifft]" falsch?
Bin ich der einzige, der bei diesem Satz schmunzeln musste? Substantiv X Verb Y ist wenn Substantiv X[...] Verb Y. Also: Wenn mein Computer am rechnen ist, bedeutet dass, das mein Computer am rechnen ist. Ist logisch hundert pro richtig, aber irgendwie mit null Aussage.
Und was heist sich an Objekten zu orientieren? - Das heist Objekt Orientiert zu sein.
Btw, auf meine Frage, wieso die Zusammenfassung von Daten und Methoden das coolste ist, wurde noch nicht eingegangen?
-
DEvent schrieb:
@Helium: Ist das ein scherz-Posting?
Nein, es ist ein sehr wahres Posting um zu zeigen, dass es mehr als nur C++/Java gibt.
Shade Of Mine schrieb:
Warum?
Ich rede nicht von austauschen zur Laufzeit. Ich rede einfach nur von Laden von Klassen zur Laufzeit. Warum ist es Polymorphie wenn ich die Klassen die es gibt doch eh statisch vor mir habe? Wo trennst du ab wann etwas zu statisch für Polymorphie wird.Wenn man nur mit dem Interface arbeitet und Instanzen fuer dieses Interface dynamisch mittels Reflection nachlaed, dann sehe ich das als OOP. Man koennte es auch mittels C++ Interpreter machen, der neue Templates erzeugt. Aber nur Templates, einmal kompiliert und danach nie wieder, ist keine OOP.
Was wenn ich die Templates zur Laufzeit kompiliere?
Ich rede auch nicht von Interfaces oder Reflection. Ich rede einfach nur davon, dass statisch ein Begriff ist, den es nicht mehr gibt. Ich kann _alles_ zur Laufzeit machen. Ich kann Klassen zur Laufzeit erstellen, Templates etc. Vergiss Java. Vergesst es einfach - das hat nichts mit Java oder Reflection zu tun, sondern damit dass wann etwas passiert mit der Implementierung des Features zusammen hängt, nicht mit dem Feature selber.Beispiel: ein C++ Interpreter.
Euer Problem ist: ihr bezieht OOP direkt auf eine Implementierung. Vor 20 Jahren konnte sich keiner denken, dass man zur Laufzeit kompiliert - heute ist das Standard: siehe Java. In 10 Jahren haben wir komplett neue Techniken zur Implementierung von solchen Features. Templates sind heute vielleicht noch etwas statisches - aber in C# zum Beispiel, sind die richtig dynamisch im Sinne von: wird zur Laufzeit die Klasse erstellt. Ist es jetzt dynamisch oder statisch?
Oder Generics in Java - sie fühlen sich an wie statische c++ Templates (in vieler Hinsicht) aber sie sind intern über Laufzeit Casts implementiert.
Das hat mich auch gewundert. Wie macht man das in JS?
Es geht nicht.
Man löst solche Sachen in JS anders - man hat andere Tools.
-
Shade Of Mine schrieb:
Vor 20 Jahren konnte sich keiner denken, dass man zur Laufzeit kompiliert
smalltalk wurde auch schon vor 30 jahren zur laufzeit kompiliert.
überhaupt versteh ich nicht wieso ihr (Xin) immer von c++ ausgeht, gerade wo c++ nur die klempnerversion von objektorientierter programmierung bereitstellt.
-
Mir ist gerade langweilig, deshalb ein kleines Gedankenspielchen:
class Triangle { public: void draw(); }; class Circle { public: void draw(); }; template<typename Shape> void foo(Shape* s) { s->draw(); }
Das ist ja kein OO Code, weil er statisch ist, nicht wahr?
class Triangle { function draw() {} } class Circle { function draw() {} } function foo($s) { $s->draw(); }
Ist das OO Code?
Oder was ist damit:
struct Shape { void (*draw)(); }; struct Triangle { void (*draw)(); }; struct Circle { void (*draw)(); }; void foo(void* s) { ((Shape*)s)->draw(); }
oder gar komplett ohne Klassen:
function Triangle() { this.draw=function(){} } function Circle() { this.draw=function(){} } function foo(s) { s.draw(); }
oder komplett ohne member (Nice):
class Triangle { } class Circle { } show(Tirangle s) {} show(Circle s) {}
Mich würde echt interessieren was davon OO ist und was nicht.
In meinen Augen sind diese Codes allesamt so identisch wie es unterschiedliche Sprachfeatures erlauben.Nebenbei: was ich mit dem dynamischen Laden von Code meine hat nichts mit Reflection zu tun und nichts mit Java. Man stelle sich einfach eine VM für C++ vor. Das Kompilieren findet während der Laufzeit statt - folglich kann ich während der Laufzeit neue Klassen laden - ich lade sie nämlich wie #include zur Compile zeit. Lediglich dass das kompilieren während der Laufzeit stattfindet.
Diese Mechanik ist für C++ natürlich schwer zu implementieren - für andere Systeme wie zB Java oder .NET ist das aber alltäglich - und es hat nichts mit Reflection zu tun - oder würdet ihr sagen dass ein #include bei einem C++ interpreter plötzlich Reflectionfähigkeit verleiht?
-
Hallo
man sollte OOP nicht Objektprogrammierung verwechseln. Letztere gibt es in Sprachen, die eine ganze - in sich geschlossene - Objektwelt bereitstellen wie Self oder Smalltalk.
In solchen Sprachen besteht Programmierung in der Manipulation der bereitgestellten Objektwelt.OOP ist etwas leicht Anderes - sozusagen die Kombination von 1.) Methoden der Objektprogrammierung mit 2.) klassischen Programmiertechniken (edit-compile-test[-reboot])*
Grüße
-
kleine Bemerkung schrieb:
Hallo
man sollte OOP nicht Objektprogrammierung verwechseln. Letztere gibt es in Sprachen, die eine ganze - in sich geschlossene - Objektwelt bereitstellen wie Self oder Smalltalk.
In solchen Sprachen besteht Programmierung in der Manipulation der bereitgestellten Objektwelt.OOP ist etwas leicht Anderes - sozusagen die Kombination von 1.) Methoden der Objektprogrammierung mit 2.) klassischen Programmiertechniken (edit-compile-test[-reboot])*
Grüße
Hindert dich irgendetwas diese "geschlossene" Objektwelt in C++ darzustellen?
Vielleicht sollte man den Begriff OOP fallen lassen. Man könnte alternativ allgemein von OO (Objektorientierung) sprechen. Oder etwas moderner von OOSE (objekt orientierter Software-Entwicklung), das dann alles von der Analyse über das Design bis zur Implementierung umfasst.
Ich verstehe z.T. auch den Aufruhr nicht, einmal wird darüber diskutiert was OO-P ausmacht und im nächsten Post wird darüber diskutiert wie die Verwirklichung davon in einer konkreten Programmiersprache auszusehen hat.
Für mich besteht OO aus diversen Grundkonzepten, wie Objekten, Attributen, dem Klassenbegriff etc. und erweiterten Konzepten wie Polymorphie, Mehrfachvererbung etc. dabei kommen diese Konzepte in den unterschiedlichen Stufen der SE zum Tragen (Analyse/Design).
Die OO Sprache ansich ist mir dabei völlig schnuppe sie hat mich nur bei der Implementierung in der Art und Weise zu unterstützen das sie mir die Grundkonzepte und die erweiterten Konzepte zur Verfügung stellt.
tt
PS: Die Definition ist sehr kurz und zählt nicht alle Konzepte auf, darüber könnte man seitenweise schreiben.
-
Shade Of Mine schrieb:
Xin schrieb:
Entweder ist es "OO" oder nicht "OO".
klingt sehr vereinfacht. warum muss alles schwarz oder weiß sein? kann nicht etwas grau sein?
Es muss nicht alles schwarz oder weiß sein, natürlich nicht.
"Zur Realisierung haben wir ein bißchen Listen benutzt."
"Meine Frau ist ein bißchen schwanger."
"Es hat grade ein bißchen geklingelt."Manche Dinge sind entweder so oder so und grade in der Informatik muss man sich darauf einlassen, dass für Grauzonen wenig Platz ist.
Shade Of Mine schrieb:
in c++ habe ich zB programme die teilweise aus reinem C code bestehen - weil es eben alter Code ist, der nach und nach an C++ angepasst wird. Ist jetzt alles OO oder sind nur teile OO oder ist garnichts OO?
Du kannst Probleme in OOP gelöst haben oder nicht, aber ob Du OOP benutzt hast, kann ich Dir daraus nun wirklich nicht sagen.
Shade Of Mine schrieb:
Abgesehen davon, dass es bei Dir auch schmutziges OO zu geben scheint, unterscheidest Du wenigstens zwischen OO-Sprache - sie es nicht gibt - und OO-unterstützende Sprachen. Wir sind uns diesbezüglich also einig.
Was ist schmutziges OOP?
Ich weiß nicht, was schmutziges OOP ist, Du sprichst von reinem OOP, also muss eine Deiner Grauzonen unreines OOP sein.
Shade Of Mine schrieb:
Wenn du JS programmiert hast - dann erklär mir mal fix wieso du dort keine OO hast. Oder hast du sie doch - wenn ja, dann widersprichst du dir ja, weil es in JS keine virtuellen Funktionen gibt. Es gibt kein dynamisches dispatchen.
Ich glaube, es ist ein Posting her, wo ich wiedereinmal betonte, dass man nicht virtual benötigt, um OOP zu programmieren, aber virtual benötigt, um mit C++ unterstützt OOP zu programmieren.
Shade Of Mine schrieb:
Worauf ich hinaus will ist: du ziehst einen Trennstrich den es nicht mehr gibt. Moderne VMs wie Java und .NET erlauben es die Compiletime der Runtime gleichzusetzen. Es gibt keine "das ist statisch" und "das ist dynamisch" Situationen mehr. Denn die Kompilierung findet während der Laufzeit statt und ich kann jederzeit neue Elemente nachladen.
Ich habe noch nie versucht final-Funktionen mit Reflection zu überladen.
Ansonsten sind dort alle Funktionen virtual, also erzwungenes OOP - im Gegensatz zu C++, wo Du erst "virtual" schreiben musst.Shade of Mine schrieb:
Vererbung und virtuelle Funktion geht problemlos ohne Klassen, aber nicht ohne Klassifizierung.
C hat keine Klassen und man kann trotzdem OOP verwenden.Vererbung ist für dich also auch Object Cloning?
"Meiner Definition" von Clonen wird eine Kopie angelegt. Solange nur kopiert wird... was bitte soll daran OOP sein?
memcpy ist jetzt nicht als die Basis von OOP verschrieen.
Um einen Algorithmus sich an Objekten orientieren lassen zu können, muss ja auch eine Beziehung zwischen den Objekten bestehen. Das ist bei Multimethoden nicht möglich, denn der einzige Algorithmus, der sich an Objekten orientiert, ist die Compiler, um die entsprechende Methode auszuwählen. Die Relation für den Compiler ist die Tatsache, dass er alle Objekte intern kennzeichnet, als Ableitung von "class Object" oder durch eine VTable. Für den Programmierer bleibt da nichts über, wenn er seinen Part (in C++) nicht über als virtual kenntlich machen würde. Darum sind Multimethoden in C++ mit Objekten ohne virtual auch nicht realisierbar. Die Compilerhersteller können schließlich auch nicht zaubern.In C++ realisiert man die Beziehungen durch Vererbung. virtual ohne Vererbung ist möglich, aber wenn man sich nur an einem Objekt orientieren kann, dann braucht man kein OOP - es ist sogar dumm OOP dazuzuschalten, weil es statisch schneller geht.
Ohne Beziehung zwischen den Objekten, gibt es keinen Algorithmus, der von der Beziehung über OOP profitieren kann. Daher ist der Vorschlag von Scrub_ leider ein Griff ins Klo:
scrub_ schrieb:
2. Der folgende Codeschnipsel ist OOP, weil abhängig vom Objekt verschiedene Funktionen gerufen werden.
class Katze { public: void GibLaut() { std::cout << "Miau"; } }; class Hund { public: void GibLaut() { std::cout << "Wau"; } }; Katze k; Hund h; h.GibLaut(); k.Giblaut();
Der Code ist kein OOP, die Objekte stehen in keinem Verhältnis zu einander, den der Compiler nachvollziehen kann. Die Basisklassifizierung "Tier" ist nur dem Entwickler bekannt. Man kann keinen Algorithmus schreiben, der sich am Objekt orientiert => nicht OOP.
void algorithmus( Tier & tier ) { tier.GibLaut(); }
otze hat sich ein wenig kleverer angestellt und stellt nicht die Behauptung auf, es wäre OOP - er fragt nach:
otze schrieb:
Xin schrieb:
Das Objekt ist eine Katze, aber handelt nicht wie eine Katze. Es ist nicht Objektorientiert, weil nach dem Referenztyp (Tier) entschieden wird Tier::GibLaut() aufzurufen, statt Katze::GibLaut().
Das ist aber nur logisch falsch, denn der Referenztyp kann diese Aufgabe ebenso erfüllen.
class Tier { private: string laut; public: Tier():laut("Hallo"){} Tier(const string& laut):laut(laut){} void gibLaut(){std::cout<<laut<<"\n";} }; class Katze: public Tier { public: Katze():Tier("Miau"){} }; int main() { Tier* tier = new Katze(); tier->GibLaut(); }
Das Ergebnis gibt das was wir erwarten. Das objekt verhält sich dem Vertrag entsprechend und gibt uns den geforderten laut zurück. logisch und semantisch korrekt
Ist das nun OOP?Bevor ich das nun verneine und erkläre, warum ich das verneinen muss, muss ich sagen, dass das bisher definitiv der kreativste Vorschlag ist. Darum werde ich das ausführlicher erklären.
Obwohl die Ausgabe identisch ist, das Programm logisch und semantisch korrekt ist, sind die Programme deswegen nicht semantisch identisch. Es ist wichtig das zu verstehen. Du hast ein anderes Problem gelöst, das zwar die identische Ausgabe produziert, aber trotzdem nicht das Problem ist zu dem Du einen identischen Code abliefern wolltest. Und das Problem, dass Du gelöst hast, benutzt kein OOP.Du beziehst Dich auf folgende Äußerung:
otze schrieb:
Xin schrieb:
Das Objekt ist eine Katze, aber handelt nicht wie eine Katze. Es ist nicht Objektorientiert, weil nach dem Referenztyp (Tier) entschieden wird Tier::GibLaut() aufzurufen, statt Katze::GibLaut().
Das ist aber nur logisch falsch, denn der Referenztyp kann diese Aufgabe ebenso erfüllen.
Du rufst in beiden Fällen Tier::GibLaut(), die dann Daten aus dem Objekt auf den Bildschirm ausgeben.
Du rufst nicht vom Objekt abhängig unterschiedlichen Funktionen. Dein Algorithmus arbeitet also nicht objekt(-typ) orientiert, er gibt lediglich Daten des Objektes aus.
Du kannst die Daten jederzeit ändern, sie sind nicht typ-spezfisch. Einzelne Katzenobjekte können in Deinem Fall auch "Muuh" oder "Ia" machen. Der Laut ist nicht vom Objekttyp abhängig.
Du hast etwas geschrieben, was einen Laut speichert. Du verwaltest einen Datensatz, aber eben keinen, der objektorientiert angibt, was zu tun ist, sondern einen, der das Objekt beschreibt. Du kannst also Katzen beschreiben, die "Muuh" machen und das ist nicht identisch zum Ursprungsproblem.Welche Funktionalität in OOP verwendet werden soll, ist dynamisch vom Objekttyp des Objektes abhängig - nicht vom Objekt selbst und auch nicht vom Referenztyp. Alle Katzen miauen und weil das alle Katzen machen, ist das statisch für Katzen festgelegt. Die statische Funktion besitzt aber keinen Namen, die dem Programmierer bekannt ist, er spricht sie über GibLaut an() und OOP sorgt dafür, dass für Katzen-Objekte GibLaut() in der VTable auf die entsprechende Funktion für Katzen verweist.
Wer nun meint, dass ich mich hier um Kopf und Kragen rede (und ich vermute, dass den semantischen Unterschied nicht jeder gleich bemerkt und sich das ganze komplizierter liest als es ist), dem sei das ganze wie folgt verdeutlicht:
Katzen sollen über den Lautsprecher laut geben. Bei Hunden blinkt zusätzlich der Bildschirm, um den User zu erschrecken, bei Pferden wird der Laut auf den Drucker ausgegeben und bei Spinnen, wird er über das Netz versand (Schenkelklopfer, was für ein Wortspiel...).
Für jedes Objekt muss eine vom Objekttyp - nicht vom Objekt - abhängige Funktion gerufen werden und das geschieht in Deinem Beispiel leider nicht, in der virtual Lösung aber schon. Die OOP-Lösung ist also flexibler als Deine Lösung und das macht den semantischen Unterschied aus.
Du bist aber sehr nah dran, objektorientiert zu arbeiten. Du musst Dich nur darauf konzentrieren, Dir Funktionalität zu merken, statt Daten. Wenn man definitiv nie mehr als nur eine Funktion objektorientiert braucht, geht das sogar schneller als virtual zu verwenden, weil man eine Dereferenzierung und eine Addition spart.
Bei mehr als einer Funktion und mehr als 3 Objekten diesen Typs, verbraucht sie allerdings mehr Speicher als die virtual-Lösung.Sieht nicht ganz so schön aus, weil ich auf virtual verzichte, ist aber semantisch gleichwertig zu meinem Beispiel mit virtual: Die Funktion wird abhängig vom Objekt und nicht vom Referenztyp ausgewählt.
#include "stdio.h" class Tier { private: static void gibLautImplementation( void ) { printf( "Hallo\n" ); } protected: Tier( void (* const gibLautImp )(void) ): gibLaut( gibLautImp ) {} public: Tier():gibLaut( &gibLautImplementation ) {} void (* const gibLaut)( void ); }; class Katze : public Tier { private: static void gibLautImplementation( void ) { printf( "Miau\n" ); } public: Katze() : Tier( &gibLautImplementation ) {} }; int main( void ) { Tier * tier = new Katze(); tier->gibLaut(); }
---------------------
Ich werde die Diskussion hier nicht beenden, werde mich vereinzelt auch gerne noch teiligen, ich möchte aber zu einem Ende anregen.
Wir diskutieren hier seit 25 Seiten und es unwahrscheinlich, dass hier noch eine Einigung gefunden wird - weniger aus argumentativen Gründen, denn der Tatsache, dass die meisten Menschen, wenn sie sich lange in etwas verbissen haben, auch dann nicht loslassen, wenn sie damit untergehen. Niemand möchte sein Gesicht verlieren.
Meine Frage von Seite 11 hat immernoch keiner beantwortet, ich habe nichtmals gesehen, dass es jemand mit /der/ Standard-Definition versucht hat.
Ich ziehe ein positives Fazit zu diesem Thread.
Ich denke, ich konnte mich über die ganzen Seiten gut behaupten und abgesehen von dem Patzer mit den Multimethoden eine konsistente und schlüssige Grundlage bieten, die von einigen anerkannt wurde und von anderen nicht.
Mich freut, dass "meine" Definition von einigen richtig verstanden wurde und mich freut ebenfalls, dass andere kein Argument vorlegen konnten, um mich an meiner Überzeugung zweifeln zu lassen.
Weiterhin habe ich die Hoffnung, dass diejenigen, die zur Zeit *die* Definition verteidigen, zur Ruhe kommen können, wenn der Thread vorbei ist und sich gelegentlich nochmal kritisch damit auseinandersetzen können.Dennoch fand ich es sehr lehrreich, denn ich musste meine Definition auf vielerlei Anregungen von euren Anfragen immer wieder prüfen und fand für alles widerspruchsfreie Antworten (ob die jeder verstanden hat - ob aus Verbissenheit zur "üblichen" Definition oder aus meinen mangelnden Fähigkeiten, sie besser zu erklären sei mal dahingestellt).
In diesem Sinne besten Dank an alle Beteiligten, doch mir fehlt etwas die Zeit und kann nicht weiter so viel Zeit in diesen Thread stecken.
-
"Hindert dich irgendetwas diese "geschlossene" Objektwelt in C++ darzustellen?"
Primitive Datentypen zum Beispiel sind kein Objekt und haben daher mit OO nichts am Hut. Vergleich in Java, primitive Datentypen und Wrapper Klassen (int vs Integer)
Eine geschlossene Objektwelt kann man in C++ zwar realisieren, man hätte dann aber quasi eine neue Sprache entwickelt, es wäre nur ein OO Aufsatz auf eine nicht vollkommen OO Sprache.
-
Shade Of Mine schrieb:
Mir ist gerade langweilig, deshalb ein kleines Gedankenspielchen:
class Triangle { public: void draw(); }; class Circle { public: void draw(); }; template<typename Shape> void foo(Shape* s) { s->draw(); }
Das ist ja kein OO Code, weil er statisch ist, nicht wahr?
Korrekt, kein OOP.
Shade Of Mine schrieb:
class Triangle { function draw() {} } class Circle { function draw() {} } function foo($s) { $s->draw(); }
Ist das OO Code?
Nein, das ist ein Template.
Statt wie C++ Code zu erzeugen, werden sie in PHP direkt interpretiert. Hat $s keine Funktion draw() knallt's halt.Shade Of Mine schrieb:
Oder was ist damit:
struct Shape { void (*draw)(); }; struct Triangle { void (*draw)(); }; struct Circle { void (*draw)(); }; void foo(void* s) { ((Shape*)s)->draw(); }
Das ist OOP, aber grausam. (hatte ich in einem vorherigen Posting auch schon mit char Arrays beschrieben)
Was Du da allerdings beschreibst ist SEHR risikoreich, da Du in C++ nicht garantieren kannst, dass sich die Variable show von Shape an der gleichen Stelle im Objekt ist, wie in Circle oder Triangle.
Darum ist Vererbung wichtig, um das zu garantieren.Shade Of Mine schrieb:
oder gar komplett ohne Klassen:
function Triangle() { this.draw=function(){} } function Circle() { this.draw=function(){} } function foo(s) { s.draw(); }
Template.
Edit: Du legst hier übrigens zwei Klassen an, also "ganz ohne Klassen" passt hier nicht ganz.Shade Of Mine schrieb:
oder komplett ohne member (Nice):
class Triangle { } class Circle { } show(Tirangle s) {} show(Circle s) {}
Was das ist, weiß ich auch nicht!? Was soll das werden?
OOP ist es jedenfalls nicht.
-
ich antworte dir mal in umgekehrter reihenfolge um besser deutlich zu machen, was ich meine:
Du kannst die Daten jederzeit ändern, sie sind nicht typ-spezfisch. Einzelne Katzenobjekte können in Deinem Fall auch "Muuh" oder "Ia" machen. Der Laut ist nicht vom Objekttyp abhängig.
Du hast etwas geschrieben, was einen Laut speichert. Du verwaltest einen Datensatz, aber eben keinen, der objektorientiert angibt, was zu tun ist, sondern einen, der das Objekt beschreibt. Du kannst also Katzen beschreiben, die "Muuh" machen und das ist nicht identisch zum Ursprungsproblem.Nein, ich kann keine katzen beschreiben die "Muuh" machen, weil die einzige stelle an dem "laut" gesetzt wird, der Konstruktor von Tier ist, und der ist ganz klar von seinem Aufruf und in diesem Konkreten Fall von Katze abhängig. Da Katze aber im konstruktor keinen Laut als argument nimmt sondern eine konstante zeichenkette an den Konstruktor von Tier übergibt, ist es ohne nicht portablen compilerhack nicht möglich, die ausgabe von katze zu ändern. Der Laut ist somit an den Typ von Katze gebunden.
Du rufst in beiden Fällen Tier::GibLaut(), die dann Daten aus dem Objekt auf den Bildschirm ausgeben.
Du rufst nicht vom Objekt abhängig unterschiedlichen Funktionen. Dein Algorithmus arbeitet also nicht objekt(-typ) orientiert, er gibt lediglich Daten des Objektes aus.Es ist richtig, dass ich nur den Datensatz "laut" ausgebe. Aber was ist der unterschied zwischen einem string und einem funktionszeiger? das sind beides nur datensätze, und alles was auf den string zutrifft, trifft auch auf deinen Funktionszeiger zu(meinetwegen mach bei meinem beispiel den ctor mit dem argument auch protected). Wenn du sagst, dass jeder den string ändern kann, kann auch jeder den Funktionszeiger ändern.
Und wnn du selbst sagst, dass funktionszeiger eigentlich auch nichts anderes als eine selbst gebaute vtable sind, dann haben wirs doch schon. Es besteht kein unterschied zwischen Typabhängigem- und Wertabhängigem Objektverhalten. Virtual ist kein verpflichtendes Kriterium für OOP.
-
"Statt wie C++ Code zu erzeugen, werden sie in PHP direkt interpretiert. Hat $s keine Funktion draw() knallt's halt."
Die fehlende Typsicherheit hat nichts mit OO zu tun, OO schreibt nicht vor, dass es typsicher sein muss. Es gibt auch OO Sprachen da würde an der Stelle garnichts passieren.
Nur weil es knallen kann und irgendwer ein Objekt "falsch" benutzt ist dies kein Zeichen mangelnder OO.
Ich würde eher kritisieren, dass foo eine globale Methode ist und nicht in einer Klasse, bzw. einem Objekt gekapselt wird.
-
otze schrieb:
ich antworte dir mal in umgekehrter reihenfolge um besser deutlich zu machen, was ich meine:
Du kannst die Daten jederzeit ändern, sie sind nicht typ-spezfisch. Einzelne Katzenobjekte können in Deinem Fall auch "Muuh" oder "Ia" machen. Der Laut ist nicht vom Objekttyp abhängig.
Du hast etwas geschrieben, was einen Laut speichert. Du verwaltest einen Datensatz, aber eben keinen, der objektorientiert angibt, was zu tun ist, sondern einen, der das Objekt beschreibt. Du kannst also Katzen beschreiben, die "Muuh" machen und das ist nicht identisch zum Ursprungsproblem.Nein, ich kann keine katzen beschreiben die "Muuh" machen, weil die einzige stelle an dem "laut" gesetzt wird, der Konstruktor von Tier ist, und der ist ganz klar von seinem Aufruf und in diesem Konkreten Fall von Katze abhängig.
Otzes Code:
class Tier { private: string laut; public: Tier():laut("Hallo"){} Tier(const string& laut):laut(laut){} void gibLaut(){std::cout<<laut<<"\n";} }; class Katze: public Tier { public: Katze():Tier("Miau"){} };
otze schrieb:
Da Katze aber im konstruktor keinen Laut als argument nimmt sondern eine konstante zeichenkette an den Konstruktor von Tier übergibt, ist es ohne nicht portablen compilerhack nicht möglich, die ausgabe von katze zu ändern. Der Laut ist somit an den Typ von Katze gebunden.
Und die Verwendung eben dieses:
int main() { Tier* tier = new Katze(); tier->laut = "Iaaa Iaaa"; tier->GibLaut(); }
Keine Ahnung, warum ich grade nicht "Muuh" schrieb, muss mich bei dem schweren, nicht portablen Compiler-Hack wohl grade vertan haben.
Edit: Ich ziehe das Ia zurück und behalte es für mich...
Du rufst in beiden Fällen Tier::GibLaut(), die dann Daten aus dem Objekt auf den Bildschirm ausgeben.
Du rufst nicht vom Objekt abhängig unterschiedlichen Funktionen. Dein Algorithmus arbeitet also nicht objekt(-typ) orientiert, er gibt lediglich Daten des Objektes aus.Es ist richtig, dass ich nur den Datensatz "laut" ausgebe. Aber was ist der unterschied zwischen einem string und einem funktionszeiger? das sind beides nur datensätze, und alles was auf den string zutrifft, trifft auch auf deinen Funktionszeiger zu[/quote]
Dann zeig mir mal, wie Du einen String aufrufst, ohne einen Segmentation fault zu bekommen.otze schrieb:
Wenn du sagst, dass jeder den string ändern kann, kann auch jeder den Funktionszeiger ändern.
Du disqualifizierst Dich soeben für das Lob, dass ich Dir über saubere Beispielcodes gegeben habe.
Du kannst Deinen String const setzen, aber den notwendigen Funktionsruf bekommst Du mit einem beliebigen Datenelement nicht hin. Du bist für OOP gezwungen Funktionszeiger zu speichern.
Meine Lösung ist kein gutes Beispiel, die Funktionszeiger sollten eigentlich statisch hinterlegt werden und nicht im Objekt auftauchen. Dann hat man virtual nachimplementiert. Das war mir als Beispielcode für zwei Klassen allerdings grade zu aufwendig.