Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

21

06.02.2014, 06:58

Wie gesagt: Die Variante mit freien Funktionen unterstützt automatisch auch int + Vector2D, sobald Vector2D einen Konstruktor anbietet, der einen int nimmt, du brauchst nicht für alle möglichen Kombinationen separate Operatoren schreiben
Das ist doch hier total irrelevant und er hat Dich sogar explizit gebeten nicht solche Fallbeispiele zu verwenden. Bezieh Dich doch bitte einfach mal auf die Ausgangssituation. Die ist eben mit zwei identischen Operatoren, die auch keinerlei implizite Konvertierungen anbieten.

Der von dir verlinkte Artikel ist schön und gut und vor allem lang. Statt aber mal direkt zu sagen, warum mehr Member die Kapselung senken und non-member sie erhöhen, sagt er einfach, dass das so ist. Und dem stimme ich nicht zu, denn Funktionalität, die von einer Klasse angeboten wird, sollte meiner Meinung nach (und das verstehe ich auch unter Kapselung) von dieser Klasse erledigt werden und nicht von freien Funktionen. Freie Funktionen können weiß der Geier was für Funktionalität an Operatoren bieten, die die Klasse aber nicht so vorgesehen hat. Z.B. kann ich für komplexe Zahlen eine Addition anbieten. Da gibt es aber nur eine sinnvolle Definition für in der Mathematik. Und diese sollte die Klasse auch abbilden und nicht zulassen, dass außerhalb jemand einen Operator hinpackt, der was ganz anderes anstellt. Bzw. schön und gut, dass eine freie Funktion eine andere Addition anbietet, aber sie macht im Kontext der Klasse eventuell gar keinen Sinn. Eine sinnvolle Implementierung des Operators wird aber aus meiner Sicht immer von der Klasse angeboten, denn diese weiß, was wirklich zu tun ist.

Ich lasse mich gern mit Argumenten vom Gegenteil überzeugen, aber bisher gab es keine Argumente außer immer wieder: "Aber schau mal, dass da kann auch implizite Konvertierung" - Und das ist bei einem == mit zwei identischen Operanden-Typen ohne irgendwelche Möglichkeit für implizite Konvertierungen bisher noch immer total sinnlos.
Das ist in etwa so als frage jemand, warum x/y nicht erlaubt ist, falls y==0 und alle anderen kommen her und erzählen ihm, dass Division eine ganz tolle Operation ist, wenn man mal davon ausgeht, dass y!=0 ist. Das geht aber eben total an der Frage vorbei. Die Antworten stellen immer eine ganz andere Ausgangssituation her, obwohl genau diese für die Frage aber relevant ist.

Man könnte sogar noch weiter gehen und eine implizite Konvertierung hier sogar als Fehlerquelle bezeichnen.
Man stelle sich vor, dass man einen Fehler in der Implementierung begeht und folgendes prüft:
if (mySize == myWindow)...
Während Window einen Konstruktor mit Size als Parameter bietet und die beiden Fenster am Ende vielleicht sogar gleich sind, wird mir doch wohl jeder zustimmen, dass eine Size und ein Window niemals gleich sein dürfen, schon weil es grundverschiedene Dinge sind. Das kann sicher auch mit einem Member so passieren, aber nur in den 50% der Fälle, wo man die Operanden in der "falschen" Reihenfolge vergleicht.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

Dieser Beitrag wurde bereits 6 mal editiert, zuletzt von »BlueCobold« (06.02.2014, 07:26)


dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

22

06.02.2014, 11:12

Der von dir verlinkte Artikel ist schön und gut und vor allem lang. Statt aber mal direkt zu sagen, warum mehr Member die Kapselung senken und non-member sie erhöhen, sagt er einfach, dass das so ist.

Der erste Teil des Artikels beschäftigt sich rein mit der Frage, wie man den Grad der Kapselung definieren/bestimmen kann (Menge an Code, der durch Änderungen an der Klasse kaputtgeht). Der Rest des Artikel enthält dann eine ausführliche Argumentation, wie genau Nonmember zu eine höheren Grad an Kapselung verhelfen können (es wird aufgezeigt, wie sie einer Reduktion der Menge an Code, der durch Änderungen an der Klasse kaputtgeht, dienlich sein können). Was genau geht dir dabei ab? Wenn du natürlich mit seiner Definition von "Kapselung" nicht einverstanden bist, dann ist das ein Problem. Dann verstehen wir beide aber wohl auch etwas unterschiedliches unter "Kapselung" und reden die ganze Zeit aneinander vorbei...

Und dem stimme ich nicht zu, denn Funktionalität, die von einer Klasse angeboten wird, sollte meiner Meinung nach (und das verstehe ich auch unter Kapselung) von dieser Klasse erledigt werden und nicht von freien Funktionen.

Du bist also der Meinung, dass sämtliche Arten und Weisen, in der jemals jemand vielleicht einmal std::string würde verwenden wollen, am besten durch spezielle Memberfunktionen von std::string implementiert würden? ;)

Wie stehst du eigentlich zu Extension Methods in C#? ;)

Freie Funktionen können weiß der Geier was für Funktionalität an Operatoren bieten, die die Klasse aber nicht so vorgesehen hat.

Dann ist das Interface der Klasse kaputt, denn es sieht offenbar die falsche Verwendung der Klasse vor.

Z.B. kann ich für komplexe Zahlen eine Addition anbieten. Da gibt es aber nur eine sinnvolle Definition für in der Mathematik. Und diese sollte die Klasse auch abbilden und nicht zulassen, dass außerhalb jemand einen Operator hinpackt, der was ganz anderes anstellt.

Da hast du ein perfektes Beispiel gefunden. Ich kann reelle Zahlen und komplexe Zahlen addieren. Ich kann rationale Zahlen und komplexe Zahlen addieren. Ich kann ganze Zahlen und komplexe Zahlen addieren. Ich kann natürliche Zahlen und komplexe Zahlen addieren. Ich kann...

Durch freie Funktionen kann ich einen entsprechenden operator + für komplexe Zahlen hinzufügen, ohne dass die Klassen für komplexe, reelle, rationale etc. Zahlen alle einander kennen und voneinander abhängig sein müssen...

Bzw. schön und gut, dass eine freie Funktion eine andere Addition anbietet, aber sie macht im Kontext der Klasse eventuell gar keinen Sinn.

Wie gesagt: Eine Klasse, die es einem Benutzer ohne Anwendung physischer Gewalt erlaubt, die Klasse in Arten und Weisen zu verwenden, die im Kontext des durch die Klasse modellierten Konzepts keinen Sinn machen, ist eine schlechte Klasse. Der Fehler ist dann ganz eindeutig bei der Klasse zu suchen und nicht bei der freien Funktion.

Eine sinnvolle Implementierung des Operators wird aber aus meiner Sicht immer von der Klasse angeboten, denn diese weiß, was wirklich zu tun ist.

Siehe Beispiel mit komplexen Zahlen oben. Das würde bedeuten, dass jede Klasse von allen anderen Klassen, mit denen sie jemals interagieren soll, abhängig wird, da sie diese bereits in ihrem Interface erwähnen muss. Interaktion mit anderen Typen ist dann unmöglich, ohne alle beteiligten Klassen einander vorzustellen. Goodbye Kapselung, Erweiterbarkeit, Modularität, Flexibilität...

Die Antworten stellen immer eine ganz andere Ausgangssituation her, obwohl genau diese für die Frage aber relevant ist.

Vielleicht sollten wir die Frage, die hier ursprünglich gestellt wurde, nochmal in Erinnerung rufen:

Warum macht man Globale Operatoren wenn beide Parameter der selben klasse Angehören?

Das von Koschi gebrachte Beispiel war etwas unglücklich gewählt, aber er konnte es ja nicht besser wissen...

Man könnte sogar noch weiter gehen und eine implizite Konvertierung hier sogar als Fehlerquelle bezeichnen.

In der Tat. Ich sage weder, dass implizite Konvertierungen immer etwas Gutes sind und alles mindestens eine haben muss (das Schlüsselwort explicit gibt es nicht umsonst, bestes Beispiel ist std::vector), noch, dass man am besten alle Methoden durch freie Funktionen ersetzt. Das konkrete Beispiel von sf::VideoMode ist eines der Beispiele, wo es wohl rein technisch tatschlich keinen Unterschied macht. Hier ging es aber nicht um dieses konkrete Beispiel, sondern um eine allgemeine Frage. Und ich persönlich würde auch in solchen Fällen, wo es rein technisch egal ist, aus Gründen der Ästhetik die Implementierung als freie Funktion bevorzugen, einfach weil == konzeptionell eine symmetrische Operation ist und die Implementierung durch Memberfunktionen eine inhärente Asymmetrie aufweist...

Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von »dot« (06.02.2014, 11:21)


BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

23

06.02.2014, 11:49

Was genau geht dir dabei ab?
Mir geht dabei gar keiner ab. Argumentum ad hominem?

(es wird aufgezeigt, wie sie einer Reduktion der Menge an Code, der durch Änderungen an der Klasse kaputtgeht, dienlich sein können)
Und das ist eben Unfug. Würde der Operator als Member anderen Code kaputt machen, wenn die Klasse geändert wird (und somit das Interface offenbar nicht mehr stimmt), wird dies genauso auch mit einer freien Funktion passieren, da diese ebenfalls vom Interface abhängt. Gewonnen habe ich also nichts. Im Gegenteil vielleicht sogar - es wird die freie Funktion durch menschliches Versagen nicht angepasst, weil sie einfach übersehen wurde, was bei einem Member vermutlich mit deutlich geringerer Wahrscheinlichkeit passiert wäre.

Du bist also der Meinung, dass sämtliche Arten und Weisen, in der jemals jemand vielleicht einmal std::string würde verwenden wollen, am besten durch spezielle Memberfunktionen von std::string implementiert würden? ;)
Nein. Aber wenn ich eine Operation von string von vornherein vorsehe, dann ist ein Member ala "substring"[x,y] oder "charAt"==[] immer noch sinnvoller als eine freie Funktion dafür, die auf den chars des strings extern rumhantiert.

Wie stehst du eigentlich zu Extension Methods in C#? ;)
Finde ich prima. Da geht es ja darum fehlende Funktionalität zu ergänzen. Da macht eine freie Funktion ja auch wieder Sinn, weil es gar nicht anders geht. Wenn ich aber von Anfang an eine Klasse schreibe und dann im selben Zug auch noch Operationen dafür, dann brauche ich da keine Extension Method, sondern definiere einen Member. Falls es darum geht eine Operation auf mehreren Datentypen zu erstellen, die alle dasselbe Interface implementieren, dann ist - ganz offensichtlich - eine Extension Method oder freie Funktion dafür deutlich besser in der Lage als ein Member, der eben nur an eine einzelne Klasse gebunden ist. Das ist hier aber gar nicht das Thema.

Dann ist das Interface der Klasse kaputt, denn es sieht offenbar die falsche Verwendung der Klasse vor.
Bitte? Das Interface der Klasse umfasst die freie Funktion gar nicht. Daher kann ich immer Dinge mit Operatoren auf Klassen tun, die total schwachsinnig sind. Wenn nicht, zeig mir eine Klasse, wo ich keinen freien Operator so definieren kann, dass er keinen Mist ausspuckt, obwohl das Interface der Klasse nicht "kaputt" ist.
Noch wilder wird es meiner Meinung nach, wenn die freie Funktion für den Operator als friend vorliegt. Friend wird an so vielen Stellen verteufelt, eben weil da eine Logik gekapselt wird, die entweder klar macht, dass die Klasse zu wenig kapselt und Klassen-spezifische Logik andernorts durchgeführt wird, oder dass sie zu viel kapselt und relevante Informationen nach außen vorenthalten werden.

Da hast du ein perfektes Beispiel gefunden. Ich kann reelle Zahlen und komplexe Zahlen addieren. Ich kann rationale Zahlen und komplexe Zahlen addieren. Ich kann ganze Zahlen und komplexe Zahlen addieren. Ich kann natürliche Zahlen und komplexe Zahlen addieren. Ich kann...
Klar, kann ich. Und genau dort macht eine freie Funktion ja auch Sinn. Aber darüber diskutieren wir hier überhaupt nicht. Also du schon, die ganze Zeit. Koschi und ich aber nicht.

Durch freie Funktionen kann ich einen entsprechenden operator + für komplexe Zahlen hinzufügen, ohne dass die Klassen für komplexe, reelle, rationale etc. Zahlen alle einander kennen und voneinander abhängig sein müssen...
Total am Thema vorbei.

Wie gesagt: Eine Klasse, die es einem Benutzer ohne Anwendung physischer Gewalt erlaubt, die Klasse in Arten und Weisen zu verwenden, die im Kontext des durch die Klasse modellierten Konzepts keinen Sinn machen, ist eine schlechte Klasse. Der Fehler ist dann ganz eindeutig bei der Klasse zu suchen und nicht bei der freien Funktion.
Die freie Funktion hat mit der Klasse gar nichts zu tun. Und genau da ist das Problem. Klar kann ich viel ausrechnen, aber damit mache ich eben die Funktionsweise und mathematischen Operationen für komplexe Zahlen kaputt. Und das ist Schwachsinn.

Siehe Beispiel mit komplexen Zahlen oben. Das würde bedeuten, dass jede Klasse von allen anderen Klassen, mit denen sie jemals interagieren soll, abhängig wird, da sie diese bereits in ihrem Interface erwähnen muss. Interaktion mit anderen Typen ist dann unmöglich, ohne alle beteiligten Klassen einander vorzustellen. Goodbye Kapselung, Erweiterbarkeit, Modularität, Flexibilität...
Nein! Gott verdammt, du bist am Thema vorbei. Es geht hier NICHT um Interaktion von komplexen Zahlen mit anderen Datentypen. Sondern nur um die Interaktion ZWEITER IDENTISCHER DATENTYPEN.

Das von Koschi gebrachte Beispiel war etwas unglücklich gewählt, aber er konnte es ja nicht besser wissen...
Nein, falsch. Das war ganz absichtlich perfekt so gewählt. Das ist genau der Punkt, über den hier gefragt wird. Alle anderen Möglichkeiten sind bekannt und erörtert worden. Aber nicht der Punkt, um den es hier explizit geht.

Und ich persönlich würde auch in solchen Fällen, wo es rein technisch egal ist, aus Gründen der Ästhetik die Implementierung als freie Funktion bevorzugen, einfach weil == konzeptionell eine symmetrische Operation ist und die Implementierung durch Memberfunktionen eine inhärente Asymmetrie aufweist...
DAS ist zumindest mal ein Ansatz einer Erklärung. Aber die wurde von Anfang an ja schon angedeutet. Man macht das eben so, weil es an gewissen anderen Stellen mehr Sinn ergibt - ob die nun hier zutreffen oder nicht. Es ist also schlicht eine Konsistenz-Entscheidung und man hätte es genauso auch andersrum entscheiden können.
Es ist aber leider nur ein Ansatz einer Erklärung, weil dieser Operator eben immer symmetrisch ist, da weder verschiedene Datentypen im Spiel sind, noch implizite Konvertierung existiert.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von »BlueCobold« (06.02.2014, 12:05)


Volker_Neff

Treue Seele

Beiträge: 249

Wohnort: Hamburg

  • Private Nachricht senden

24

06.02.2014, 15:48

Ich will mich auch einmal einschalten wobei ich betonen möchte das ich mich mit Operatoren noch nicht viel beschäftigt habe. Eine didee die mir grade noch durch den Kopf gegangen ist das man eventuel als möchte das der Code einheitlich ist. Das heißt damit man zum Beispiel ein == Operator als Member implementiert und einen anderen wie den + Operator als globalen. Wenn ich mir das so überlege fallen mir mehr Operatoren die wie die +-*/ Operatoren lieber global sein sollten.

Wollte damit nur noch einmal einen Denkanstoß geben. Vieleicht ist es aber auch wie in vielen Dingen eine Sache der Gewohnheit, oder wie man es selber lieber macht.

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

25

06.02.2014, 15:50

Na ja, die einzig schlüssige Erklärung wäre eben die:
Es gibt Operationen und Datentypen, wo eine freie Funktion aufgrund der impliziten Konvertierung mehr Sinn macht und wenn man es schon bei diesen macht, macht man es gleich bei allen.

Eine reine Konsistenz-Festlegung eben. Aber nun ja.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

26

06.02.2014, 18:37

Warum macht man Globale Operatoren wenn beide Parameter der selben klasse Angehören?
als Beispiel diene hier mal etwas aus der Bekannten SFML

C-/C++-Quelltext

1
bool operator ==(const VideoMode& left, const VideoMode& right)

Also konkret gefragt welche Vorteile bringt diese Methode gegenüber der Implementierung als Member der Klasse?
Dass der Operator nicht versehentlich auf geschützte Attribute und Methoden der Parameter zugreifen kann und dadurch die Schnittstelle zerbricht. (Eigentlich das, was dot die ganze Zeit zu erklären versucht.)

27

06.02.2014, 20:00

Ich mag jetzt nicht nochmal alles wiederholen was Blue schon angebracht hat, aber auf die stelle mag ich noch mal eingehen.


Vielleicht sollten wir die Frage, die hier ursprünglich gestellt wurde, nochmal in Erinnerung rufen:
Warum macht man Globale Operatoren wenn beide Parameter der selben klasse Angehören?

Das von Koschi gebrachte Beispiel war etwas unglücklich gewählt, aber er konnte es ja nicht besser wissen...


Mein Beispiel war genau so gewollt. Über die Globalen Operatoren die 2 Unterschiedliche Operanden (2 verschiedene Klassen) haben brauchen wir eigentlich gar nicht reden, denn hier bestreitet im großen und ganzen niemand die Sinnhaftigkeit.

Mir geht es einzig und alleine um Operatoren-Funktionen bei denen beide Operanden, also der links Seitige und der rechts Seitige Operand, von der selben Klasse sind.

Zitat von »Krishty«


Dass der Operator nicht versehentlich auf geschützte Attribute und Methoden der Parameter zugreifen kann und dadurch die Schnittstelle zerbricht. (Eigentlich das, was dot die ganze Zeit zu erklären versucht.)


Also im Idealfall sind die Member einer Klasse alle private (Das verstehe ich unteranderem unter Kapselung). So kann ein Globaler Operator eigentlich schon gar nicht mehr auf die Member einer Klasse zu greifen. Die Lösung hier für, man macht eine friend-Funktion, diese wiederum hat zugriff auf alle Member einer Klasse. Man könnte hier dann ein mix machen aus private und public Membern (wenn es sinn macht) um friend-Funktion zu vermeiden, aber an diesem Punkt würde für mich jetzt die Kapselung drunter leiden!

Ich glaube auch nicht das Dot versucht hat das zu erklären (zumindest konnte ich es so nicht rauslesen aus seinen Beiträgen).

Letzten Endes bin ich für mich zu dem Schluss gekommen das es eine Frage der Philosophie des Programmierers ist.

Danke für eure Beiträge.

Gruß Koschi
Wer aufhört besser werden zu wollen hört auf gut zu sein!

aktuelles Projekt:Rickety Racquet

28

06.02.2014, 20:04

Also im Idealfall sind die Member einer Klasse alle private (Das verstehe ich unteranderem unter Kapselung). So kann ein Globaler Operator eigentlich schon gar nicht mehr auf die Member einer Klasse zu greifen. Die Lösung hier für, man macht eine friend-Funktion, diese wiederum hat zugriff auf alle Member einer Klasse.
Was verstehst du unter Lösung? Was ist das Problem?

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

29

06.02.2014, 20:34

Jetzt fängt er wieder an zu philosophieren und sich an Wörtern aufzuhängen. Manchmal frage ich mich, Krishty, was genau der Sinn der Diskussionen von dot und Dir sind. Auf zfx gibt es ja auch schon einige davon. TGGC kann wohl ein Lied davon singen.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

30

06.02.2014, 20:39

War das so schlecht beschrieben ok versuche ich es nochmal.

Wenn ich die Daten einer Klasse Kapsle (kein zugriff von außen) muss ich der Klasse Methoden mitgeben (set-Methode), um indirekten zugriff auf private Member zu ermöglichen. So kann dann ein non-friend Gloabl Operator auf die "innerein" einer Klasse zugreifen und ändern (ebenfall der ganze Rest, hat mit den setMethoden zugriff). Oder ich mache den Globalen Operator friend dann kann er ebenfalls auf alle "innerein" der Klasse zugreifen.

Ich bitte im Kopf zu behalten meine Frage die Voraus gegangen ist!

Oder ich könnte einen Operator gleich mit der Klasse anbieten, muss keine setMethoden schreiben die ich gar nicht haben will und brauch auch keinen Globalen friend Operator.

Letzteres ist für mich persönlich die sauberste Lösung was Datenkapselung angeht.

witziger weise wenn man sich mal die Klassen anschaut z.B. die VideoMode Klasseder SFML sind alle Member public.
Wer aufhört besser werden zu wollen hört auf gut zu sein!

aktuelles Projekt:Rickety Racquet

Werbeanzeige