Methode vs Klassenmethode vs globale Funktion
-
Morgen!
Ich habe eine Designfrage: Sagen wir ich habe mathematische Klassen wie Vektor und Matrix. Beide bieten natürlich verschiedene Funktionalitäten, z.B. kann man eine Matrix invertieren, transponieren etc. und aus 2 Vektoren ein Skalarprodukt berechnen. Nun frage ich mich, WO ich diese Funktionalitäten implementiere.
Nehmen wir mal als Beispiel die Inverse der Matrix. Ich sehe da 3 Möglichkeiten:
- Als Methode:
void Matrix::invert(); Benutzung: Matrix m; m.invert();2. Als statische Methode:
static Matrix Matrix::invert(const Matrix& m); Benutzung: Matrix m, m2; m2 = Matrix::invert(m);oder 3) als globale Funktion:
Matrix MatrixInvert(const Matrix& m); Benutzung: Matrix m, m2; m2 = MatrixInvert(m);Was würdet ihr empfehlen?
Zweite Frage: Die 2 Klassen bieten auch Möglichkeiten zusammen zu arbeiten, zb. die Multiplikation: Vektor * Matrix, Matrix * Vektor etc.
Wo implementiere ich solche Funktionalitäten, die 2 Klassen betreffen? (ich würde ja zu globalen Funktionen tendieren)
-
Also zum ersten Problem, würde ich defintiv auf die Klassenmethoden zurückgreifen. Schließlich sind das ja Objekteigene Funktionalitäten.
Beim zweiten Problem, wäre doch eine Hilfsklasse nicht schlecht, die diverse Möglichkeiten anbietet um solche Operationen durchzuführen.
-
Hi,
also ich habe mal gelesen: NUR, wenn
a) auf private-Members zugegriffen werden muss (weil der Zugriff über die bestehende öffentliche Schnittstelle nicht ausreicht) oder
b) virtual-Polymorphie genutzt werden soll,
sollte man eine Memberfunktion verwenden.
Ansonsten: global.Ganz so drastisch sehe ich das nicht mache ich das aber nicht. Ich "empfinde" doch Memberfunktionen noch "mehr zur Klasse gehörend" als freie Funktionen ... und ebenso einige Eigenschaften/Möglichkeiten als "mehr mit der Klasse verbunden" als andere.
Trotzdem freue ich mich immer über "kleine Klassen", die auf's Wesentliche beschränkt sind (und an deren Layout man gleich sehen kann, was sie tun/können.@2:
Ich versuche zu unterscheide zwischen "Objektmanipulatoren" und "Algorithmen": Letztere versuche ich immer aus Klassen herauszuhalten. Wenn diese "Funktionen" von Dir beide Objekte im Wesentlichen lesen und ein Ergebnis ausrechnen, würde ich sie definitiv NICHT in eine Klasse packen (auch nicht in eine "Ich_Sammle_mal_alle_moeglichen_Funktionen"-Klasse
).
Ist unintuitiv, schlechter zu erweitern, aufwendiger und vorteilsfrei.Gruß,
Simon2.
-
2. Namespace math...
-
Danke schon mal an alle Tipps

@Simon2: Ich finde diese 2 Richtlinien a) und b) auch zu drastisch, vor allem, da man ja a) mittels friend Funktionen aushebeln kann.
Zum zweiten Problem (wo ich gemeinsame Funktionalitäten hin packe): Ich fände es auch sehr unelegant eine künstliche Klasse zu schaffen, die nur existiert um Funktionen zu bündeln - vor allem, weil es in C++ ja freie Funktionen und Namespaces gibt. Ich bin mir ziemlich sicher, dass ich die Algorithmen also in freie Funktionen packen werde, die dann wohl in etwa so aussehen:
Vector VectorMul(const Vector& v, const Matrix& m);
usw. Bin allerdings weiterhin für andere Meinungen offen.
Zum 1. Problem: Ich bin am überlegen, ob ich jede Funktionalität 2mal anbiete: einmal als Methode, die das Objekt verändert und einmal als statische Methode, die nur den Parameter ändert. Dann könnte ich das machen:
m.invert();aber auch das:
m2 = Matrix::invert(m); // m2 ist die inverse von m, aber m ist nicht invertiert wordenAndererseits frage ich mich, ob das nicht bissi Overkill (alles doppelt anbieten) und unkonsequent ist. Denn ich will ja gemeinsame Funktionalitäten (siehe Problem1) als globale Funktionen implementieren. Dann hätte ich also viele Methoden + statische Methoden in den Klassen (für alle Funktionaltitäten, die nur die jeweilige Klasse betreffen) und zusätzlich viele freie Funktionen. Mein Code könnte dann z.B. so aussehen:
Matrix m; Vector v, v2; m.invert(); // hier eine Methode float f = Vector::DotProduct(v, v2); // hier eine statische Methode m*=f; v2 = VectorMul(v, m); // hier eine globale FunktionDann hätte ich also alle 3 Fälle. Findet ihr das in Ordnung? :|
-
- Freie Funktionen ala
GetXYValue( OBJ& o ) { return o.m_value; }mit friend ist absoluter Käse.
2) VectorMULTIPLIKATION -> operator*( const Vector& , const Matrix& ) ->v2 = v*m;
3)void Matrix::invert() {...} Matrix Matrix::inverted() const { Matrix m2 = *this; m2.invert(); return m2; }
-
Abwäger schrieb:
...vor allem, da man ja a) mittels friend Funktionen aushebeln kann. ...
Diese Richtlinie zielt auch nicht auf den "Zugriffsschutz", sondern auf hohe "Codeklarheit" (und den damit verbundenen Vorteilen).
... und letztlich bringt das Argument "kann man ja aushebeln mit ..." irgendwie nicht weiter, weil man alles irgendwie hebeln.
Funktionalitäten "2mal anbieten" finde ich ehrlich gesagt kernblöd. (no offense)

Was versprichst Du Dir denn davon?
Ich als Nutzer einer Klasse werde eher verwirrt, wenn es mehrere Funktionen gibt, die anscheinend (oder scheinbar?) dasselbe machen.@invert: Auch wenn man oft davon spricht, "eine Matrix zu invertieren", meint man damit meiner Ansicht nach etwas anderes: "Eine neue Matrix B zu erzeugen, die die Inverse zur vorgegebenen Matrix A ist". Deshalb ist die Invertierung für mich kein Kandidat für eine Memberfunktion.
Auch in der Benutzung stelle ich mir das anstrengend vor. Dass ich die Inverse einer Matrix brauche, bedeutet in vielen Fällen nicht, dass ich die "Orignalmatrix" nicht mehr brauche.
Dann müsste man folgendes machen:// macht etwas mit einer Matrix und ihrer Inversen void f(matrix a, matrix inv_a); int main() // irgendwoher habe ich Matrix a... matrix orig_a = a; a.invert(); // ist jetzt die Inverse von a f(orig_a, a); // später will ich noch mit a weiterarbeiten // und muss dann entscheiden: Nehme ich "orig_a" // oder kopiere ich wieder nach a ? ...... insgesamt eher blöd, finde ich.
Macht man ja ehrlich gesagt in Mathe auch nicht so - da bekommt die Inverse ja üblicherweise auch einen neuen Namen.Gruß,
Simon2.
-
Matrix::invert(target,src); //nocopy version und einzige implementiierung. bei vielen kleinem //matrizen ist nocopy vielleicht von vorteil algorithm::matrix_invert(target,src) //klingt bescheuert, aber warum nicht? dann ist obige ein wrapper. target=src.invert(); //nur ein wrapper für die erste. sonst springen die javanesen ab. target=invert(src); //nur ein warpper für die erste, sonst springen die mathemantiker ab.
-
Ich nehme mal an, dass du einen Elementzugriff in der Matrix hast? Dann würde ich der Empfehlung von Scott Meyers folgen:
Desto weniger Zugriffe auf private Member, desto stärker ist die Kapselung, desto stärker ist die Objektorientierung.
Folglich kann man den Aufbau der Matrixklasse verändern und solange man den Elementzugriff weiterhin erlaubt, muss an derinvertFunktion nichts verändert werden.Ich würde also invert als non-member non-friend Funktion erstellen und das möglichst im gleichen Namespace wie die Matrixklasse selber, damit ADL zieht.
namespace math { class Matrix { // ... }; Matrix invert(Matrix const& src); // Wenn man will, kann man noch eine optimierte // Variante anbieten: Matrix& invert(Matrix const& src, Matrix& dest); } // math // Verwendung: math::Matrix matrix; math::Matrix invertedMatrix = invert(matrix); // Zu beachten: Man muss für invert den Namespace nicht angeben, // da hier ADL angewendet werden kann.Grüssli
-
Du kasnnstja mal schauen, wie andere das schon gemacht haben: http://www.boost.org/doc/libs/1_35_0/libs/numeric/ublas/doc/index.htm