starcow hat geschrieben: ↑11.03.2021, 13:51Krishty hat geschrieben: ↑11.03.2021, 11:53
xq hat geschrieben: ↑11.03.2021, 11:41Ne, das liegt daran, dass du auch gerne aus anderen Dateien auf "Point.x" zugreifen möchtest
Dann macht man sich einen Getter, der
Point const & als Parameter erwartet. Der Getter ist zusammen mit
Point implementiert. Der Compiler braucht außerhalb dieser Implementierung keine Größe kennen.
Genau so funktioniert C. In C++ kann man es auch machen, aber eben nicht mit Methoden, sondern nur mit freien Funktionen. Und das ist grundsätzlich kaputt.
Krishty, du bist für mich sowas wie ne Kompetenz-Bestie! :->
Ich hab das Beispiel jetzt leider noch nicht ganz verstanden...
Also, im wesentlichen programmierst du C mit ein paar "sanften" C++ Ergänzungen? Als nach wie vor ein Anfänger in vielen Fragen, kann man sich da an dich halten?
Besser nicht – ich stecke ziemlich tief in solchem Kram drin; meine Ratschläge sollte man nur befolgen, wenn man auch versteht, was sie tun. Als ob dir ein Rennfahrer erzählt, dass es sich mit gesperrtem Differential besser fährt, und dann wurschtelst du lustig am Auto rum und wunderst dich, warum du nicht mehr einparken kannst.
Du kennst das vielleicht, dass du eine Klasse komplett definieren musst, bevor du sie verwenden kannst:
class Point {
public:
…
int getX() const { return x; }
int getY() const { return y; }
private:
int x;
int y;
};
Aber in C/C++ kann ich eine Datenstruktur deklarieren, ohne dass ich sie definiere:
struct Point;
Ich kann auch Funktionen deklarieren, die diese Datenstruktur nutzen. Dafür muss die Datenstruktur nicht definiert, sondern nur deklariert sein:
int getX(Point);
int getY(Point);
Das löst erst einen Compiler-Fehler aus, sobald ich
Point tatsächlich instanziere (z. B. als lokale Variable oder Parameter) oder
getX()/
getY() aufrufe.
Was bringen mir ein Typ und zwei Funktionen, die ich nicht benutzen kann?! Nicht viel. Aber änder den Parameter der Getter von Value zu Reference:
int getX(Point const &);
int getY(Point const &);
… und nun kannst du
getX() und
getY() benutzen, ohne die Implementierung von
Point zu kennen. Wo immer du ein
Point als Zeiger oder Referenz bekommst, kannst du
getX() und
getY() aufrufen – obwohl
Point dem Aufrufer nie definiert wurde. Die Definition liegt irgendwo weit weg, wo auch die Getter definiert sind.
Und das reduziert die Abhängigkeiten. Hast du eine Monsterklasse
Player mit Gettern für Position, Sounds, Grafik, und so weiter – dann brauchen die ganzen abhängigen Klassen nicht definiert sein. Die Deklarationen der Datenstrukturen reichen aus,
und du kannst sogar damit arbeiten, so lange du keine Instanzen kopierst oder neu anlegst. Dein
SoundManager (oder was auch immer) braucht kein
#include "Graphics" mehr, bloß, weil das in
Player vorkommt.
Das löst nicht alle Probleme, aber schonmal ganz schön viele.
Und das funktioniert nur mit freien Funktionen, nicht mit Methoden. Denn Methoden müssen in der Klasse deklariert sein. Und da müssen auch die privaten Member hin. Wenn du also
Point::getX() aufrufen willst, muss
Point vollständig definiert sein,
samt aller Abhängigkeiten. Und wenn das ebenfalls Klassen mit Methoden sind, auch
samt der Abhängigkeiten der Abhängigkeiten usw. Jetzt hast du ein exponentielles Problem und den Grund, warum C++ länger kompiliert als die meisten anderen Sprachen.
Bei
getX(Point const &) gibt es den Ärger nicht. Dafür gibt es IMO keinen vernünftigen Grund (außer
„als wir das 1985 implementiert haben, war das halt so“). Darum finde ich
class in C++ scheiße und das C++-Buildsystem grundsätzlich kaputt.
(Auch virtuelle Funktionen sind kein Grund: Ich könnte theoretisch jede Funktion vom Compiler hinter einem automatisch generierten, nicht-virtuellen Wrapper verstecken lassen und die Entscheidung, ob es ein virtueller Aufruf ist oder nicht, zur Implementierung verschieben.)
Um den Bogen nach oben zu schlagen: Das bedeutet
nicht „Krishty sagt, benutzt keine Klassen!“ :) Es bedeutet:
„Bedenk, was du dem Rest des Projekts antust, wenn du class schreibst. Und wäg es dann mit deinen Zielen ab.“