> > Vererbung ist eine Methode, um Variationen
> > im Programmverhalten an einer Stelle formulieren zu können und um
> > Erweiterungen zu ermöglichen ohne allen Code ändern zu müssen.
>
> Solchen Code hab ich auch schon refactoren "dürfen". Das mit der
> Objektorientierten Softwarearchitektur üben wir bitte noch mal.
Pfff.
> Vererbung drückt grundsätzlich eine "is a" Beziehung der
> entsprechenden Klassen aus.
Unabhängig von Mehrfachvererbung ist diese Reduktion auf "is a" oder
"acts like" problematisch. Ein Quadrat "is a" Rechteck in der "echten
Welt", aber wenn man versucht, dass so in Code abzubilden, bekommt
man lauter hässliche Effekte - man hat mehr Instanzvariablen als man
braucht, setWidth() ergibt keinen Sinn mehr, usw. Das kann man zwar
noch unter einen Hut kriegen, wenn man das Programm als
Momentaufnahme betrachtet, aber sobald dann später der Entwickler vom
Rechteck Methoden hinzufügt, die mit den Kantenlängen spielen, geht
die Implementierung von Quadrat kaputt weil nicht mehr sichergestellt
werden kann, dass Kante a immer = b ist.
Es bringt einem nichts, auf philosophischer Metaebene die
Zusammenhänge der Welt zu analysieren und zu versuchen, daraus Code
zu gießen. Vererbung ist ein Sprachfeature was zur Reduktion von
Komplexität und zur Entkopplung von Code sehr nützlich sein kann.
Aber mehr halt auch nicht.
> meiner inzwischen nicht mehr so ganz geringen Erfahrung ist mir noch
> nie eine Situation untergekommen, in der eine gute Struktur für das
> Problem einen Diamanten erzeugt hätte.
Auch jenseits von Diamant-Vererbung kann der Methodlookup
nicht-intuitive Konsequenzen haben. A erbt von B und C, C benutzt
eine Methode intern die B überschreibt - durch das Vererben in A
ändert sich die Implementierung von Cs Funktionalität. Das ist zwar
alles nachvollziehbar, aber halt nicht offensichtlich.
> Kannst ja mal bei den KDE Leuten nachfragen, warum die immer noch das
> meiste in C++ machen.
Die haben halt keine vernünftige Alternative. Java für
Enduser-Desktop-Anwendungen funktioniert aus verschiedenen Gründen
(Startup-Zeit, Integration) nicht gut, und C#/Mono wollen die wohl
nicht.
Ich halte das auch für kein wirklich gutes Argument - KDE gab es ja
schon lange bevor attraktivere Sprachen vorhanden waren, die haben
auch einfach legacy. Und nach meinen (wenigen) Erfahrungen mit KDE
haben die auch ganz hübsche Stabilitätsprobleme.
> > Pointerarithmetik,
>
> Damit kann man manchmal sehr effiziente Sachen machen. Insofern hat
> es seine Berechtigung, dass es das gibt. Man muss es ja nicht
> verwenden, wenn man es nicht braucht.
Selbst wenn man es nicht benutzt verhagelt es eine ganze Reihe von
Compiler-Optimierungen. Daneben führt es zu inhärent unsicheren
Programmen. Die ganze Effizienz-Geschichte war sicherlich mal wahr,
aber siehe Go - laut Ken Thompson gibt es keinen Grund mehr,
Pointer-Arithmetik zu erlauben. Moderne Compiler kriegen das
Boundary-Checking auch so wegoptimiert.
> > manuelles Memory-Management,
>
> Niemand hält dich davon ab, einen Garbage Collector einzusetzen. Da
> gibt es gute Libraries dafür.
Aber es gibt aufgrund der Pointer vs. int Problematik nur
konservative GC Bibliotheken (zB Boehm), und die sind nicht nur
langsam sondern eventuell auch inkorrekt. Abgesehen davon passt ja
auch Garbage Collection nicht zu einer Programmiersprache, die
Destruktoren zum Resource Cleanup benutzt.
> Du kannst auch
> beides für verschiedene Objekte in der gleichen Anwendung einsetzen
> und wieder andere Objekte "von Hand" verwalten.
Schon klar, aber der Punkt ist, dass ich mich weiter darum kümmern
muss. Komplexität die einfach unnötig ist.
> Ich hab bei einem früheren Projekt mal das Problem gehabt, mit PHP
> aud Shared Memory zuzugreifen. Die entsprechenden Funktionen sind da,
> aber wie bekommt man die Objekte da dann rein? Man muss sie
> serialisieren. Das macht dann irgendwie den Sinn von Shared Memory
> lächerlich. Wenn man seinen Speicher also nicht selber verwalten
> darf, stößt man immer mal wieder auf derartige Probleme.
Das in PHP so ziemlich alles lächerlich ist, ist ja nicht wirklich
eine Neuigkeit.
> > Operatoren überladen,
>
> Dazu habe ich mein Standardbeispiel:
> [Komplexe Zahlen]
Ja, aber jenseits von komplexen Zahlen und Matrizen wird es ganz dünn
mit den Beispielen. Operator-Überladung funktioniert nur gut auf
Werten die einen Ring bilden, im mathematischen Sinne. In meiner
Praxis als Programmierer ist mir das genau nie über den Weg gelaufen.
Das ist wieder Komplexität, die eigentlich unnötig ist. Wenn komplexe
Zahlen & Matrizen eine wichtige Domäne für die Programmiersprache
sind, dann sollte man sie halt in die Sprache integrieren, und gut
ist.
> > Stack vs. Heap Allocation,
>
> Wo ist das Problem?
Das Problem ist, dass ich mich darum kümmern muss, und es zu subtilen
Probleme führt wenn es jemand falsch macht. Das ist eine
Compiler-Optimierung und sollte kein Benutzer-sichtbares Feature der
Programmiersprache sein.
> Das ist tatsächlich suboptimal. Wenn man versucht, weitgehend C
> Kompatibel zu sein, muss man solche Kröten eben schlucken.
Warum das so kam ist klar, nur muss ich es deshalb ja nicht gut
finden.
> ordentlichen Codekonventionen lässt sich das Problem in den Griff
> bekommen.
> > dangling pointers,
>
> Eine Folge schlechten Memory Managements.
Ja. Aber wieder Komplexität, die nicht nötig gewesen wäre. Und in
jedem nicht trivialen Projekt rennt man irgendwann in solche Probleme
rein. Die Zeit, die man dann mit debugging verbringt könnte man auch
in Features oder Freizeit investieren.
> > mäßige libraries,
>
> Was genau fehlt dir, oder ist dir nicht gut genug?
Boost und stdlib sind ja im Prinzip ganz nett, aber wenn du das mit
der Class Library bei Java, C#, Python oder Ruby vergleichst hat es
nicht im entferntesten vergleichbare Funktionalität. Das kann man
sich sicher in C++ auch zusammensuchen, aber "batteries included" ist
ein feature.
> > include files,
>
> Wo ist das Problem?
Ewige Compile-Zeiten, Abhängigkeiten von der Include-Reihenfolge, der
ganze Dependency-Wahnsinn.
Wo wir grade vom Compiler sprechen: ein tolles feature eine
Programmiersprache ist auch, wenn man Source Code tatsächlich mit
vertretbarem Aufwand parsen kann. Das hat bei Java zu einer
unglaublichen Vielfalt geführt - hervorragende IDEs, statische
Codeanalyse, Generators, usw.
> > der ganze const Schlamassel.
>
> const erlaubt dem Compiler sehr effiziente Optimierungen. Es ist also
> sinnvoll, das auch in der Sprache zu haben. Wo ist dein Problem
> damit?
Extrem unintuitive Deklarationen, und dann doch wieder volatile?
In Sprachen ohne Pointerarithmetik und mit echter Laufzeitumgebung
kriegt der Compiler solche Optimierungen auch ohne Babysitting durch
den User hin.
> > .NET und Java nehmen so viel unsinnige Komplexität aus der
> > Softwareentwicklung,
>
> Naja, wenn ich mir die Java Romane anschaue, ist das nicht unbedingt
> klarer ausgedrückt als ein ordentlich gemachtes C++ Programm.
Schlechten Code kriegt man in jeder Sprache hin. Aber .NET und Java
nehmen dem Entwickler extrem viel Komplexität ab, so dass er seinen
Hirnschmalz darauf verwenden kann, besseren Code zu schreiben anstatt
sich Sorgen ums Memory management zu machen. Das gilt auch für Python
oder Ruby, wobei man sich da dann wieder Sorgen um die Performance
machen muss...
> > und die Performance ist praktisch gleichwertig.
>
> Hab ich bislang keinen sinnvollen Beleg für diese Behauptung gesehen.
> Jedenfalls nicht so allgemein. Mag sein, dass es Anwendungsfälle
> gibt, wo es tatsächlich gleichwertig ist, aber allgemein glaube ich
> das nicht.
Das Problem ist immer, dass man beim "benchmarketing" praktisch alles
und nichts beweisen kann. Es gab auf jeden Fall schon Benchmarks in
denen Java schneller war, und im Allgemeinen würde ich vermuten, dass
"normale" Anwendungen (was auch immer das sein mag) sich in der
Größenordnung 1:2 bewegen. Das würde ich für die Vereinfachung der
Entwicklungsarbeit ohne mit der Wimper zu zucken in Kauf nehmen -
wenn man sich ausrechnet, was die Entwicklerarbeitszeit relativ zu
der Hardware kosten würde, ist die Entscheidung leicht.
Es gibt auch eine ganze Reihe von eher theoretischen Argumenten,
warum Java code im Prinzip gleichwertig bis schneller sein sollte. GC
zB sollte prinzipiell zu schnellerem Code führen, und dynamische
Optimierungen zur Laufzeit, spekulative Optimierung usw. sollten
generell auch zu schnellerem Code führen. Die C++ Compiler haben
natürlich noch einigen Vorsprung, aber ich würde erwarten, dass
High-Level Sprachen mit Laufzeitumgebung in den nächsten Jahren
abgesehen von Nischenanwendungen überholen.