Ansi C Operator überladen?
-
Marc++us schrieb:
Sprachmittel unvernünftig einzusetzen lässt sich durch die Sprache selbst nicht wirklich verhindern. Schaffst Du das Mittel ab, finden findige Programmierer andere Wege, ihre Programme obfuscated zu machen.
klar, aber dann macht man's ja mit absicht.
mit op-überladung baut man sich obfuscation ein, obwohl man's eigentlich gut meint.Gast++ schrieb:
- "Missbrauch von '>>' und '<<' in der STL"
Genau diese Konstrukte waren für mich vor zehn Jahren der Grund es mal mit C++ zu versuchen.
im ernst? ich fand an C++ (damals) konstruktoren/destruktoren virtuelle funktionen und vererbung toll.
'<<' und '>>' fand ich aber schon immer schrecklich.Gast++ schrieb:
Ein
printf("Hausnummer:%i\t Name:%s\n",42,"RMS")
isr sicherlich schwerer lesbar als das iostream-Pendant.
naja, kommt drauf an. wenn man bei cout erst anfängt mit 'manipulators' herumzufummeln, dann geht's mit printf oft einfacher. cout versucht ja auch anhand des typs die richtige ausgabe zu machen, was z.b. bei 'volatile' voll daneben geht.
Gast++ schrieb:
- Effizienz
"String + String ist ein Funktionsaufruf, a + b nicht" - das vergleicht Äpfel mit Birnen....
so ist es. denn op-overloading lässt äpfel wie birnen und birnen wie äpfel aussehen
Gast++ schrieb:
Und ob aus
int c = a + b;
nun wirklich einfach
push eax mov eax,a add eax,b mov c,eax pop eax
wird, mag ich auch noch bezweifeln.
vielleicht nicht exakt so, wie du's hier zeigst, aber annähernd so, jedenfalls ohne irgendwelche calls dazwischen.
Gast++ schrieb:
Für mich ist es ein Qualitätsmerkmal einer allgemeinen Klasse wenn mehrere überladene Operatoren für sie definiert sind - z.B. gearde
ostream& operator<<(...)
weil sie dann viel schlanker zu verwenden ist.
schlank? ja, du musst wenig quelltext schreiben um diese funktionalität zu nutzen, aber was compiler und linker daraus machen ist alles andere als schlank
- "Missbrauch von '>>' und '<<' in der STL"
-
pale dog schrieb:
so ist es. denn op-overloading lässt äpfel wie birnen und birnen wie äpfel aussehen
Was ist der Unterschied zwischen einem Funktionsaufruf und sonst irgend einem Code? Ich sehe da keinen fundamentalen Unterschied.
-
Mr. N schrieb:
pale dog schrieb:
so ist es. denn op-overloading lässt äpfel wie birnen und birnen wie äpfel aussehen
Was ist der Unterschied zwischen einem Funktionsaufruf und sonst irgend einem Code? Ich sehe da keinen fundamentalen Unterschied.
der wichtigste unterschied ist die zeit, die funktionsaufrufe brauchen.
stell dir vor, du müsstest ~1MB byteweise von einem buffer an irgendeine hardware schaufeln oder umgekehrt. jeder funktionsaufruf in dem code würde zu 1040000 funktionsaufrufen:for (long n=0; n<1040000; n++) send_over_spi (array[n]);
mal angenommen 'array' ist eine schicke, nach allen regeln der OOP designten klasse, der operator[] ist überladen und macht einen bereichs-check
wenn man glück hat, kann man die funktionsaufrufe mit inline weg kriegen. ist aber nur glück, denn 'inline' heisst für den c++ compiler: 'mach mir bitte-bitte-bitte keinen funktionsuafruf rein', doch wenn der compiler nicht will denkt er 'leck mich' (und schweigt sich drüber aus).
aber angenommen er ist so gnädig und macht es doch, dann hast du immer noch die bereichsprüfung drin, die alles ausbremst.
und um beim thema zu bleiben: dem [] sieht man's nicht an...
...aber trotzdem behaupten viele immer noch, C++ wäre für hardwarenahe programmierung geeignet
-
pale dog schrieb:
der wichtigste unterschied ist die zeit, die funktionsaufrufe brauchen.
Etwa 5 Takte. Außer natürlich ein Page Fault (schlimmstensfalls mit Laden ausm Swap) kommt dazwischen, aber das is ja auch bei normalem Speicherzugriff so.
pale dog schrieb:
stell dir vor, du müsstest ~1MB byteweise von einem buffer an irgendeine hardware schaufeln oder umgekehrt. jeder funktionsaufruf in dem code würde zu 1040000 funktionsaufrufen:
for (long n=0; n<1040000; n++) send_over_spi (array[n]);
Das ist auch ohne array als Objekt bescheuert.
pale dog schrieb:
mal angenommen 'array' ist eine schicke, nach allen regeln der OOP designten klasse, der operator[] ist überladen und macht einen bereichs-check
Sowas macht doch keiner
... Oder halt nur als assert, aber das is dann ja egal.
pale dog schrieb:
wenn man glück hat, kann man die funktionsaufrufe mit inline weg kriegen. ist aber nur glück, denn 'inline' heisst für den c++ compiler: 'mach mir bitte-bitte-bitte keinen funktionsuafruf rein', doch wenn der compiler nicht will denkt er 'leck mich' (und schweigt sich drüber aus).
Der Compiler entscheidet da in der Regel sehr vernünftig. Anhand so Regeln wie (keine Garantie, ob die zutrifft):
Ist der vollständig optimierte Funktionscode größer oder kleiner als 64 KB? Wenn er kleiner ist, wird er geinlinet, sonst nicht. Sowas finde ich vernünftig.
pale dog schrieb:
aber angenommen er ist so gnädig und macht es doch, dann hast du immer noch die bereichsprüfung drin, die alles ausbremst.
Klar. Hat man aber nicht
.
pale dog schrieb:
und um beim thema zu bleiben: dem [] sieht man's nicht an...
Doch, man sieht sofort, dass das ein potenzieller Funktionsaufruf ist, wenn das Objekt von keinem Primitivtypen ist. Und den Typen muss man ja eh kennen.
Übrigens: Für long long / __int64 wird auf deinem x86 bestimmt auch zur Multiplikation ein Funktionsaufruf verwendet. Ähnlich bei float und double auf Maschinen ohne FPU.
pale dog schrieb:
...aber trotzdem behaupten viele immer noch, C++ wäre für hardwarenahe programmierung geeignet
Das ist jetzt polemisch...
-
pale dog schrieb:
...
der wichtigste unterschied ist die zeit, die funktionsaufrufe brauchen....Also nach einer Diskussion um Sprachkonsistenz kommst Du jetzt mit Performance ?
Und die soll schlechter werden, wenn man dem User ermöglicht (wohlgemerkt: Nicht zwingt !!), eigene Varianten zu schreiben ?Sehe ich wirklich nicht.
Gruß,
Simon2.
-
pale dog schrieb:
Mr. N schrieb:
pale dog schrieb:
so ist es. denn op-overloading lässt äpfel wie birnen und birnen wie äpfel aussehen
Was ist der Unterschied zwischen einem Funktionsaufruf und sonst irgend einem Code? Ich sehe da keinen fundamentalen Unterschied.
der wichtigste unterschied ist die zeit, die funktionsaufrufe brauchen.
Aber den braucht er ja auch, wenn man keine Operator-Überladung sondern eine Methode genommen hätte...
(ganz zu schweigen davon, dass der Compiler den Operator ja inlinen kann...)
-
Mr. N schrieb:
pale dog schrieb:
der wichtigste unterschied ist die zeit, die funktionsaufrufe brauchen.
Etwa 5 Takte. Außer natürlich ein Page Fault (schlimmstensfalls mit Laden ausm Swap) kommt dazwischen, aber das is ja auch bei normalem Speicherzugriff so.
das ist ja systemabhängig. auch wenn's weniger takte und alle interrupts disabled sind, es summiert sich aber auf...
Mr. N schrieb:
pale dog schrieb:
stell dir vor, du müsstest ~1MB byteweise von einem buffer an irgendeine hardware schaufeln oder umgekehrt. jeder funktionsaufruf in dem code würde zu 1040000 funktionsaufrufen:
for (long n=0; n<1040000; n++) send_over_spi (array[n]);
Das ist auch ohne array als Objekt bescheuert.
ist es. aber leider hat man nicht immer einen coprozessor oder 'ne DMA-logik, die der gequälten CPU sowas abnehmen könnten.
Mr. N schrieb:
pale dog schrieb:
...aber trotzdem behaupten viele immer noch, C++ wäre für hardwarenahe programmierung geeignet
Das ist jetzt polemisch...
ja, muss auch mal sein :p
Simon2 schrieb:
pale dog schrieb:
...
der wichtigste unterschied ist die zeit, die funktionsaufrufe brauchen....Also nach einer Diskussion um Sprachkonsistenz kommst Du jetzt mit Performance ?
naja, Mr.N hat doch nach funktionsaufrufen gefragt und wann die stören könnten.
-
pale dog schrieb:
naja, Mr.N hat doch nach funktionsaufrufen gefragt und wann die stören könnten.
Nein, ich habe gefragt, wieso du bei der Unterscheidung so ne Krise kriegst.
Du bist auf das wichtigste übrigens nicht eingegangen:
Mr. N schrieb:
Doch, man sieht sofort, dass das ein potenzieller Funktionsaufruf ist, wenn das Objekt von keinem Primitivtypen ist. Und den Typen muss man ja eh kennen.
Übrigens: Für long long / __int64 wird auf deinem x86 bestimmt auch zur Multiplikation ein Funktionsaufruf verwendet. Ähnlich bei float und double auf Maschinen ohne FPU.
:p
-
Mr. N schrieb:
Du bist auf das wichtigste übrigens nicht eingegangen:
Mr. N schrieb:
Doch, man sieht sofort, dass das ein potenzieller Funktionsaufruf ist, wenn das Objekt von keinem Primitivtypen ist. Und den Typen muss man ja eh kennen.
ja, muss man auch.
man muss sein innenleben kennen, damit man damit nicht auf die nase fällt.
ich würde mir das ding ganz genau ansehen wollen. es könnte sowas sein:typedef unsigned char Array[MAXSIZE];
das wäre gut
es könnte aber auch sowas sein:class Array { ... public: int operator[](int i) { for (long s=0; l<0xfffff; s++) { // do smth. very useful here ;) } return calculated_value; } ... };
das wäre schlecht
dem array-index operator sieht man die internen schweinereien nicht an.
soviel (neben bei noch) zum thema 'information hiding'
-
pale dog, ss hat gar keinen Sinn (mehr) mit dir zu diskutieren. Auf jeder Seite sagen mehrere Leute, dass du ständig Argumente gegen die OP nennst die für alle Formen von Funktionen gelten. Aber trotzdem tust du es immer wieder...
-
pale dog schrieb:
dem array-index operator sieht man die internen schweinereien nicht an.
soviel (neben bei noch) zum thema 'information hiding'Aus diesem Grund gibt es in guter Dokumentation die O-Notation oder ein Hinweis auf die verwendete Implementierung.
Auf normalen CPUs (nicht Mikrocontroller) ist es doch relativ egal ob da nun ein paar Takte mehr oder weniger verbraucht werden, solange der Algorithmus passt. In den seltenen Fällen wo die letzten Millisekunden benötigt werden, kann man immer noch später optimieren.
-
lolz schrieb:
pale dog, es hat gar keinen Sinn (mehr) mit dir zu diskutieren.
naja, das glaube ich auch
wir haben mittlerweile alle pros und cons von op-überladung aufgezählt, haben unsere ansichten mitgeteilt und uns nicht frech dabei angepöbelt. so muss das sein.
mir fällt jedenfalls nix mehr ein, also lasst uns aufhören.
hat jedenfalls spass gemacht...
-
Spaß macht sowas immer. Schlimm wird es erst dann, wenn Gegner und Befürworter keine Einigung finden, bzw. pro und contra nicht einsehen können, sondern immer stur ihre felsenfeste Meinung durchsetzen wollen.
-
Doch, man sieht sofort, dass das ein potenzieller Funktionsaufruf ist, wenn das Objekt von keinem Primitivtypen ist. Und den Typen muss man ja eh kennen.
Mal ein kurzer Einwand, ich dachte eben darum ging es doch: Man soll den Typen nicht kennen. Ob es ein primitiver Typ ist oder nicht, man wendet die Operatoren wie gewohnt an.
Deswegen gibt es doch die primitiven Typen in modernen OOP-Sprachen doch immernoch: Man will etwas atomares, etwas "Hardwarenahes". Das hat man mit den primitiven Typen. Das wiederspricht sich aber mit Operatorüberladung: Denn Operatoren sind auch etwas atomares.
Meine Vorderung wäre: Entweder primitive Typen oder Operatorüberladungen. Wenn man Operatorüberladungen zulässt, dann soll bitte auch alles ein Objekt sein, damit es nichts mehr atomares in der Sprache gibt.
-
DEvent schrieb:
Man soll den Typen nicht kennen.
Ob ich ein int oder ein double dividiere ist nicht egal. So ergebnismäßig.
-
DEvent schrieb:
Deswegen gibt es doch die primitiven Typen in modernen OOP-Sprachen doch immernoch:
hö?
Man will etwas atomares, etwas "Hardwarenahes".
hö?
Das hat man mit den primitiven Typen.
hö?
Das wiederspricht sich aber mit Operatorüberladung: Denn Operatoren sind auch etwas atomares.
hö?
Wer ist "man"? Du?
-
DEvent schrieb:
Deswegen gibt es doch die primitiven Typen in modernen OOP-Sprachen doch immernoch: Man will etwas atomares, etwas "Hardwarenahes".
Dass primitive Typen nicht unbedingt "atomar" oder "hardwarenah" sind, wurde hier im Thread schon an Beispielen genannt.
-
Tim schrieb:
DEvent schrieb:
Deswegen gibt es doch die primitiven Typen in modernen OOP-Sprachen doch immernoch: Man will etwas atomares, etwas "Hardwarenahes".
Dass primitive Typen nicht unbedingt "atomar" oder "hardwarenah" sind, wurde hier im Thread schon an Beispielen genannt.
Das habe ich wohl übersehen. Primitive Typen sind aber in dem Sinn des Wortes atomar, da man sie nicht weiter zerlegen kann. Ein Objekt kannst du in seine Methoden und Attribute zerlegen, einen primitive Typen nicht.
@rüdiger:
Wieso ist den in Java und C# primitiven Typen wie int, float, double, usw. immernoch enthalten? Wieso gibt es Wrapper-Klassen für diese Typen? Wieso hat man nicht gleich die primitiven Typen als Objekte konzepiert, die ebenfalls von Object abgeleitet sind?
-
DEvent schrieb:
@rüdiger:
Wieso ist den in Java und C# primitiven Typen wie int, float, double, usw. immernoch enthalten? Wieso gibt es Wrapper-Klassen für diese Typen? Wieso hat man nicht gleich die primitiven Typen als Objekte konzepiert, die ebenfalls von Object abgeleitet sind?Das soll jetzt was beweisen? Gerade in Java und C# sind die Builtin-Typen doch nicht hardwarenahe oder atomar.
-
DEvent schrieb:
Primitive Typen sind aber in dem Sinn des Wortes atomar, da man sie nicht weiter zerlegen kann. Ein Objekt kannst du in seine Methoden und Attribute zerlegen, einen primitive Typen nicht.
Naja, Threads sehen das leicht anderes und sowieso geht folgendes:
union Foo{ int a; char b[sizeof(int)]; };
Obwohl int primitive ist kann es noch weiter zerlegt werden. :p
Den Zusammenhang mit Operatoren ergibt sich mir jedoch nicht. Erwartet irgend jemand ernsthaft von:
Matrix<2, 2> a, b; a += b;
dass += sich auf genau eine Prozessorbefehl abbilden lässt?