Ist es eine gute Praxis, Getter und Setter inline zu machen?

75
public:
     inline int GetValue() const {
          return m_nValue;
     }
     inline void SetValue(int nNewValue) {
          this -> m_nValue = nNewValue;
     }

In Learn C ++ sagten sie, dass es schneller laufen würde. Also dachte ich, es wäre großartig, es auf Getter und Setter anzuwenden. Aber vielleicht gibt es einige Nachteile?

Martijn Courteaux
quelle
Vielen Dank an alle! Allgemeine Schlussfolgerung: Tun Sie es nicht, der Compiler wird sich darum kümmern.
Martijn Courteaux
1
Es hängt davon ab, ob. Wenn Sie sie in der Klassendefinition haben, müssen Sie sie nicht einbinden, da sie bereits standardmäßig vorhanden sind. Wenn Sie die Implementierung in einer separaten CPP-Datei durchführen, liegt es am Linker , der wie auf berühmten Plattformen intelligent sein kann, oder einfach an einem dummen Linker, der auf weniger bekannten AFAIK-Plattformen nichts einbindet.
AraK
4
Lassen Sie mich ein paar Worte zu der Antwort hinzufügen, die ich unten gegeben habe. Persönlich mag ich es nicht, meine Klassendeklaration mit Code zu überladen, da ich dies als Teil der (technischen) Dokumentation betrachte. Gleiches Argument für die Methodendefinition in der Header-Datei, jedoch nicht so schlecht. Ah und schließlich: Brauchen Sie wirklich Getter und Setter? :-)
mkluwe
2
@mkluwe +1 Ich stimme zu, Getter und Setter sind selten Teil einer guten Praxis.
Daramarak
1
@ Daramarak: Eigentlich sind es meistens die Setter, die schlechte Praxis sind.
André Caron

Antworten:

65

Ich inline nichts, bis mir ein Profiler ausdrücklich gesagt hat, dass kein Inlining zu einem Leistungsproblem führt.

Der C ++ - Compiler ist sehr intelligent und wird diese einfache Funktion mit ziemlicher Sicherheit automatisch für Sie einbinden. Und normalerweise ist es schlauer als Sie und kann viel besser bestimmen, was eingefügt werden soll oder nicht.

Ich würde es vermeiden, darüber nachzudenken, was ich tun soll oder nicht, und mich auf die Lösung konzentrieren. Das inlinespätere Hinzufügen des Schlüsselworts (was keine Garantie für Inline-BTW ist) ist sehr einfach und potenzielle Stellen können mit einem Profiler leicht gefunden werden.

JaredPar
quelle
47
Der Compiler kann nichts einbinden, wenn Sie seine Implementierung in eine separate Quelldatei einfügen. Nicht relevant für das vorgestellte Beispiel, aber dennoch erwähnenswert.
Mark Ransom
22
Tatsächlich bieten sowohl GCC als auch VS Inlining zwischen Quelldateien an.
Welpe
7
@Mark - das ist irgendwie wahr, da es zu diesem Zeitpunkt tatsächlich der Linker ist, nicht der Compiler.
Edward Strange
2
@DeadMG, ich habe noch nie davon gehört und kann mir nicht vorstellen, wie es implementiert werden würde. Hast du einen Link?
Mark Ransom
3
Vielleicht könnte der Linker die Arbeit erledigen, wenn sich beide Dateien im selben Projekt befinden. Wenn Sie jedoch eine vorkompilierte DLL verknüpfen, kann sie nichts einbinden, was nicht explizit in den Header-Dateien enthalten ist.
mmmmmmmm
30

Wenn Sie sie in die Definition schreiben, werden sie inline standardmäßig berücksichtigt .

Dies bedeutet , dass sie (erscheinen sich typischerweise in mehreren Übersetzungseinheiten seit Klassendefinitionen) in mehreren Übersetzungseinheiten zugelassen werden, nicht , dass sie tatsächlich werden inlined.

AraK
quelle
4
Wenn der Compiler sie inline will ...
mk12
5
Der C ++ - Standard berücksichtigt die Funktionen, inlinewenn sie in der Klassendefinition definiert sind, die fast nichts mit dem Compiler / Linker zu tun hat, der eine Funktion einfügt.
Mooing Duck
16

Dies ist eine schlechte Vorgehensweise in öffentlichen APIs. Jede Änderung dieser Funktionen erfordert eine Neukompilierung aller Clients.

Im Allgemeinen zeigt Getter und Setter eine schlechte Abstraktion, tun Sie es nicht. Wenn Sie ständig zu den Rohdaten in einer anderen Klasse wechseln, müssen Sie wahrscheinlich Ihre Klassen neu anordnen, stattdessen überlegen, wie Sie die Daten innerhalb einer Klasse bearbeiten möchten, und geeignete Methoden dafür bereitstellen.

Greg Domjan
quelle
6
Könnten Sie die schlechte Abstraktion näher erläutern? Was genau meinst du und wie spiegelt es sich in diesem Beispiel wider? sollte m_nValue nur öffentlich sein?
Stijn
7
@stijn: Eine Klasse sollte eine Abstraktion von etwas sein. Die Implementierung sollte keine Rolle spielen, und die Operationen sollten für das, was sie darstellt, von Bedeutung sein. Es sollte Klasseninvarianten geben: Aussagen, die für die Klasse immer zutreffen. Getter und Setter machen die Implementierung fast genauso verfügbar wie öffentliche Variablen. Nach meiner Erfahrung sind die Werte einzelner Variablen in Bezug auf die Klassenabstraktion in der Regel nicht aussagekräftig, sodass Sie Operationen für die Klasse zulassen, die für ihre Abstraktion nicht relevant sind. Getter und Setter machen es schwieriger, Invarianten zu halten.
David Thornley
64
Hyper-dogmatischer Unsinn. Sie glauben nicht, dass Sie die Länge einer Zeichenfolge ermitteln müssen? Text auf einer UI-Schaltfläche ändern? Aktuelle Koordinaten der Maus abrufen? Sicherlich ist es möglich, Getter und Setter zu missbrauchen, wie es für jedes Muster gilt. Aber "mach es nicht" als pauschale Regel ist IMO schlechter Rat.
user168715
3
Ich denke, jeder hat einen überempfindlichen Gegenschlag. Es ist klar, dass Container Accessoren haben (also über Referenzen gleichwertig): Es ist ausdrücklich Teil ihres Zwecks, bestimmte veränderbare Werte zu streiten. Es ist auch klar, dass die meisten Klassen keine Container sind. Das Problem mit Ratschlägen "im Allgemeinen, tu es nicht" ist, dass es schwierig ist, den Rat zu verwenden - zu jedem Beispiel eines guten Getter / Setzers lautet die Antwort "Ich sagte, tu es im Allgemeinen nicht, nicht tun". tu es nicht, wenn es eine gute Idee ist ". Der Rat ist also genau gleichbedeutend mit "im Allgemeinen nur Dinge tun, die unter bestimmten Umständen eine gute Idee sind" ;-p
Steve Jessop
3
@Steve: Vielleicht können wir uns etwas Nützlicheres einfallen lassen. Hier ist mein Vorschlag: Entwerfen Sie nicht in Getter und Setter. Entwurfsfunktionen, die für die Abstraktion nützlich sind. Es ist beispielsweise sinnvoll, von der X-Koordinate eines Punktes oder der Länge einer Zeichenfolge zu sprechen. Wenn sich herausstellt, dass es sich im Wesentlichen um Getter und Setter handelt, ist dies einfach zu implementieren.
David Thornley
10

Negative Punkte:

  1. Der Compiler kann Sie ignorieren.

  2. Jede Änderung dieser Funktionen erfordert eine Neukompilierung aller Clients.

  3. Ein guter Compiler wird ohnehin nicht inline Funktionen einbinden, wenn dies angemessen ist.

Edward Strange
quelle
5

Ich möchte auch hinzufügen, dass es ziemlich irrelevant ist, ob diese inline sind oder nicht, es sei denn, Sie führen Millionen von Gets / Sets pro Frame durch. Es lohnt sich ehrlich gesagt nicht, den Schlaf zu verlieren.

Denken Sie auch daran, dass nur weil Sie das Wort "inline" vor Ihre Deklaration + Definition setzen, dies nicht bedeutet, dass der Compiler Ihren Code inline macht. Es werden verschiedene Heuristiken verwendet, um herauszufinden, ob dies sinnvoll ist. Dies ist häufig der klassische Kompromiss zwischen Geschwindigkeit und Größe. Es gibt jedoch das Brute-Force-Schlüsselwort '__forceinline' in VC ++ (ich bin mir nicht sicher, was es in GCC ist), das auf die ausgefallenen Heuristiken des Compilers stampft. Ich empfehle es wirklich überhaupt nicht und außerdem wird es wahrscheinlich falsch sein, wenn Sie einmal auf eine andere Architektur portiert haben.

Versuchen Sie, alle Funktionsdefinitionen in die Implementierungsdatei aufzunehmen und die reinen Deklarationen für die Header zu belassen (es sei denn, Sie sind natürlich eine Vorlagen-Metaprogrammierung (STL / BOOST / etc). In diesem Fall befindet sich so ziemlich alles in den Headern;))

Einer der klassischen Orte, an denen Leute gerne inline arbeiten (zumindest in Videospielen, von denen ich komme), sind mathematische Überschriften. Kreuz- / Punktprodukte, Vektorlängen, Matrixlöschung usw. werden häufig in der Kopfzeile platziert, was ich nur für unnötig halte. 9/10 macht es keinen Unterschied für die Leistung, und wenn Sie jemals eine enge Schleife durchführen müssen, z. B. ein großes Vektorarray durch eine Matrix transformieren, ist es wahrscheinlich besser, die Mathematik manuell inline durchzuführen oder sie sogar besser zu codieren plattformspezifischer Assembler.

Oh, und noch ein Punkt: Wenn Sie der Meinung sind, dass eine Klasse wirklich mehr Daten als Code enthalten muss, sollten Sie eine gute alte Struktur verwenden, die das OO-Gepäck der Abstraktion nicht mit sich bringt. Dafür ist sie da. :) :)

Tut mir leid, ich wollte nicht so viel weitermachen, aber ich denke, es hilft, reale Anwendungsfälle zu berücksichtigen und mich nicht zu sehr auf pedantische Compiler-Einstellungen einzulassen (vertrau mir, ich war dort;))

Viel Glück.

Shane

Shane
quelle
1
Ein weiterer Kommentar hier: Wenn Sie Inlining erzwingen, riskieren Sie die Möglichkeit, eine Schleife zu groß zu machen und möglicherweise einige Cache-Probleme zu haben. Leistungsprobleme können bei modernen CPUs nicht intuitiv sein. Wenn Sie Inline erzwingen möchten, führen Sie einen Leistungstest mit und ohne Inline durch und behalten Sie die erzwungene Inline nur bei, wenn dies hilfreich ist.
David Thornley
3

Indem Sie den Code in die Kopfzeile einfügen, legen Sie Ihre internen Klassenarbeiten offen. Kunden können dies sehen und Annahmen darüber treffen, wie Ihre Klasse funktioniert. Dies kann es schwieriger machen, Ihre Klasse später zu ändern, ohne den Clientcode zu beschädigen.

Tim Rupe
quelle
2

Der Code wird etwas länger kompiliert und Sie verlieren die Kapselung. Alles hängt von der Größe des Projekts und seiner Art ab. In den meisten Fällen ist es in Ordnung, sie inline zu machen, wenn sie keine komplexe Logik haben.

Übrigens können Sie überspringen, inlinewenn Sie direkt in der Klassendefinition implementieren.

Paul
quelle
2

Das Inline-Schlüsselwort ist in Ihrem Fall bedeutungslos

Der Compiler wird Ihre Funktion einbinden, wenn dies möglich und möglich ist, unabhängig vom Schlüsselwort.

Das Schlüsselwort inline wirkt sich auf das Verknüpfen und nicht auf das Inlining aus. Es ist ein bisschen verwirrend, aber lesen Sie es nach.

Befindet sich die Definition in einer anderen Kompilierungseinheit (im Grunde genommen Quelldatei nach Präprozessor) als der Aufruf, ist Inlining nur möglich, wenn die Optimierung des gesamten Projekts und die Generierung des Verbindungszeitcodes aktiviert sind. Durch Aktivieren wird die Verknüpfungszeit erheblich verlängert (da praktisch alles im Linker neu kompiliert wird), die Leistung kann jedoch offensichtlich verbessert werden. Nicht sicher, ob es in GCC und VS standardmäßig aktiviert oder deaktiviert ist.

Gal
quelle
1

Keine Notwendigkeit, vertrauen Sie den Compilern, zumindest für solche Optimierungen!
"Aber nicht immer"

Numan
quelle
1

Ich muss sagen, ich habe nicht die starke Abneigung gegen diese Praxis, die andere in diesem Thread zu haben scheinen. Ich stimme zu, dass der Leistungsgewinn durch Inlining in allen außer den am häufigsten verwendeten Fällen vernachlässigbar ist. (Und ja, ich bin in der Praxis auf solche Fälle gestoßen.) Wenn ich diese Art von Inlining mache, mache ich es aus Bequemlichkeitsgründen und im Allgemeinen nur für Einzeiler wie diesen. In den meisten meiner Anwendungsfälle ist die Notwendigkeit, eine Neukompilierung auf der Clientseite zu vermeiden, wenn ich sie jemals ändere, nicht so stark.

Ja, Sie können das löschen inline, wie dies durch die Platzierung der Implementierung impliziert wird.

Ich bin auch ein wenig überrascht über die Vehemenz gegen Accessoren. Man kann kaum an einer Klasse in einer OO-Sprache niesen, ohne ein paar zu verlieren, und sie sind schließlich eine gültige Technik, um die Implementierung von der Schnittstelle zu abstrahieren. Es ist also ein bisschen masochistisch, sie als schlechte OO-Praxis zu bezeichnen. Es ist ein guter Rat, Accessoren nicht wahllos zu schreiben, aber ich rate Ihnen auch, sich nicht vom Eifer mitreißen zu lassen, sie auszurotten.

Nehmen Sie das, Puristen. :-)

Owen S.
quelle