Mitgliedsfunktionen vs. Nicht-Mitgliedsfunktionen für mathematische Operatoren


12

Ich schreibe eine lineare Algebra-Bibliothek (kurz gesagt, eine Schulaufgabe), die Matrizen, Vektoren usw. enthält. Beim Erstellen dieser Bibliothek werden Funktionen erstellt, die mathematische Operationen an Objekten ausführen. Zum Beispiel Matrix transponieren, Matrix invertieren, Vektor normalisieren usw.

Ich war neugierig, was die "beste Vorgehensweise" für diese Art von Funktion ist ... Soll ich die Funktion als Mitgliedsfunktion oder als Nichtmitglied definieren? (Aus Gründen der Übersichtlichkeit / Bibliothek verwenden Sie Sake)

Beispiel:

//Member function way:
B = A.transpose();
C = A.inverse();

//Non-member function way:
B = linalg::transpose(A); //Non-member transpose function in linear algebra namespace
C = linalg::inverse(A);

Gibt es einen Standard für diese Art von Operationen? Oder gibt es zumindest eine gemeinsame Art und Weise, wie Menschen dies tun? Ich neige zur ersten Option, würde aber gerne wissen, ob dies empfohlen wird.

Antworten:


7

Dies ist nur eine Frage von Stil und Geschmack. Ich habe auch verschiedene Bibliotheken für lineare Algebra gesehen

  • in einem OOP-Stil mit Mitgliedsfunktionen geschrieben

  • in einem Nicht-OOP-Stil mit nur freien Funktionen geschrieben

  • Bereitstellung einer API mit beiden

und alle arbeiteten. Zumindest sollte Ihre API konsistent sein, wählen Sie also Ihre Wahl und stellen Sie sicher, dass Sie diese Stile nicht willkürlich mischen.


8

Vermeiden Sie Mitgliedsbeiträge: Wenn möglich, ziehen Sie es vor, Funktionen zu erstellen, die keine Mitglieder von Freunden sind.

Nicht-Member-Nicht-Friend-Funktionen verbessern die Kapselung, indem sie Abhängigkeiten minimieren: Der Hauptteil der Funktion kann nicht von den nicht-öffentlichen Mitgliedern der Klasse abhängen (siehe Punkt 11). Sie zerlegen auch monolithische Klassen, um trennbare Funktionen freizusetzen und die Kopplung weiter zu reduzieren (siehe Punkt 33). Sie verbessern die Generizität, da es schwierig ist, Vorlagen zu schreiben, die nicht wissen, ob eine Operation ein Mitglied für einen bestimmten Typ ist oder nicht. [...]

Herb Sutter


1
Endlich eine gute Antwort: Spät, aber nicht zu spät. ;-) Eine weitere Referenz: GOTW # 84: Monoliths "Entfesselte"
Deduplicator

1

Sowohl Mitglieder- als auch Nicht-Mitglieder-Funktionen haben neben dem bloßen Geschmack echte Vorteile. Beispielsweise matrixmüssen Sie wahrscheinlich die zugrunde liegenden Arrays verwenden, die zum Implementieren einer Klasse verwendet werden private, es friendsei denn, Sie verwenden Memberfunktionen. In dieser Hinsicht kann ein OO-Design besser sein.

Bedenken Sie jedoch, dass Informatiker von Zeit zu Zeit komplexe mathematische Formeln implementieren müssen, ohne immer zu wissen, was sie wirklich tun. Wenn ich das tun muss, bevorzuge ich Funktionen, die keine Mitglieder sind, da das "visuelle" Ergebnis näher an der ursprünglichen mathematischen Formel liegt (da mathematische Formeln im Allgemeinen einen funktionalen Stil verwenden).

Am Ende ist die Antwort dieselbe wie die von Doc Brown: Es ist Geschmackssache. Kurz gesagt, Sie könnten erwägen, eine Bibliothek im OO-Stil mit Mitgliedsfunktionen zu schreiben (einfacher zu schreiben, nein friend) und dann Nicht-Mitgliedsfunktionen zu schreiben, um dieselben Aufgaben auszuführen, bei denen die Argumente einfach an die Mitgliedsfunktionen weitergeleitet werden. Es ermöglicht Ihnen, konsistent zu sein und dem Benutzer die Wahl zu lassen, was er für am besten hält.


0

Wenn Sie tiefer in Ihre Problemstellung eintauchen, wird klar, dass Sie irgendwann benutzerdefinierte Datenstrukturen verwenden, die bestimmte mathematische Einheiten darstellen. Zum Beispiel könnten Sie eine Matrix my mittels eines n-dimensionalen Arrays darstellen. Um auf Ihr Beispiel einzugehen,

// C way
typedef struct {
    int data[MAX_LEN][MAX_LEN];
} MATRIX;

MATRIX B = transpose(A);

// C++ way
class Matrix; // Forward Declaration
class Matrix {
    int data[MAX_LEN][MAX_LEN];
    public:
        Matrix transpose();
};

Matrix B = A.transpose();

(Das C ++ - Beispiel ist vereinfacht, Sie können Vorlagen und dergleichen verwenden.)

Der zu berücksichtigende Punkt ist die einfache Verwendung von benutzerdefinierten Datenstrukturen in beiden Fällen. C ++ (oder jedes objektorientierte Programm) als Sprache ist an sich leistungsfähiger und dies gilt für den Benutzer der Bibliothek ebenso wie für den Implementierer. Ich habe in der Vergangenheit nur C-Bibliotheken verwendet, und diese benutzerdefinierten Datenstrukturen scheinen sich im Nu zu verbreiten! Während Interoperabilität mit letzteren leichter zu erreichen ist (mit speziellen Konstruktoren, vielleicht?)

Wenn Sie C ++ verwenden, ist ein objektorientierter Ansatz auf jeden Fall empfehlenswert.


5
Entschuldigung, es gibt nur sehr wenige Fakten in Ihrer Argumentation. Es gibt keinen Unterschied in der Benutzerfreundlichkeit zwischen Ihren beiden transposeBeispielen. Der Hauptgrund dafür, dass C ++ einfacher ist, ist, dass die Semantik von Kopieren und Zuweisen tatsächlich funktioniert. A = Bkann in C kompilieren, tut aber wahrscheinlich das Falsche.
MSalters

+1 für Kopiersemantik Der Punkt, den ich zu vermitteln versuchte, ist die Verwendung von Objekten (Kapseln über "Repräsentationen") v / s unter Verwendung lose gekoppelter Mengen von Strukturen und verwandten Funktionen.
Dotbugfix
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.