4 Stimmen

Warum ist es besser, func( const Class &value ) zu schreiben?

Warum sollte man die func( const Class &value ) und nicht nur func( Class value ) ? Sicherlich werden moderne Compiler mit jeder Syntax das effizienteste Ergebnis erzielen. Ist dies noch notwendig oder nur ein Überbleibsel aus den Tagen der nicht optimierenden Compiler?

  • Nur um hinzuzufügen, dass gcc für beide Syntaxen ähnliche Assembler-Code-Ausgaben erzeugt. Vielleicht tun andere Compiler nicht?

Offensichtlich ist dies nicht der Fall. Ich hatte vor langer Zeit aufgrund von Code den Eindruck, dass gcc dies tut, aber das Experimentieren beweist das Gegenteil. Der Dank geht an Michael Burr, dessen Antwort auf eine ähnliche Frage würde nominiert werden, wenn sie hier angegeben würde.

8 Stimmen

"Sicherlich werden moderne Compiler mit jeder Syntax das effizienteste Ergebnis erzielen." Computer tun nicht das, was man von ihnen will, sondern das, was man ihnen sagt!

3 Stimmen

"gcc wird für beide ähnliche Assemblercodes erzeugen" - können Sie diese Behauptung belegen? Wenn es wahr ist, hat der gcc einen Fehler.

2 Stimmen

Es ist möglich, dass gcc unter bestimmten Umständen ähnlichen Assembler-Code für beide erzeugt, vielleicht wenn ein int übergeben wird. Es sollte sicherlich nicht, wenn es eine nicht-triviale Kopie Konstruktor herum.

18voto

JaredPar Punkte 699699

Es gibt 2 große semantische Unterschiede zwischen den beiden Signaturen.

Die erste ist die Verwendung von & im Namen des Typs. Dies signalisiert, dass der Wert per Referenz übergeben wird. Wird dies entfernt, wird das Objekt als Wert übergeben, wodurch im Wesentlichen eine Kopie des Objekts an die Funktion übergeben wird (über den Kopierkonstruktor). Für Operationen, die lediglich Daten lesen müssen (typisch für eine const & ) eine vollständige Kopie des Objekts zu erstellen, verursacht unnötigen Overhead. Für Klassen, die nicht klein sind oder Sammlungen darstellen, ist dieser Overhead nicht trivial.

Die zweite ist die Verwendung von const . Dadurch wird verhindert, dass die Funktion versehentlich den Inhalt von value über die value Hinweis. Sie gibt dem Aufrufer ein gewisses Maß an Sicherheit, dass der Wert nicht durch die Funktion verändert wird. Ja, die Übergabe einer Kopie gibt dem Aufrufer in vielen Fällen eine viel tiefere Sicherheit in dieser Hinsicht.

0 Stimmen

Selbst mit einer Kopie schreibe ich normalerweise func(const Value myVal) damit ich nicht versehentlich versuche, ein temporäres Objekt innerhalb der Funktion zu ändern (es sei denn, es wird nur für diesen Zweck als Wert übergeben). Dies ist nur ein kleiner Trick, um zu vermeiden, dass man stundenlang nachverfolgen muss, warum sich der Objektzustand nach der Ausführung von func :)

8voto

John Kugelman Punkte 327535

In der ersten Form wird keine Kopie des Objekts erstellt, sondern nur ein Verweis (Zeiger) auf die vorhandene Kopie übergeben. Bei der zweiten Form wird eine Kopie erstellt, was teuer sein kann. Dies ist nicht etwas, das wegoptimiert wird: Es gibt semantische Unterschiede zwischen einer Kopie eines Objekts und dem Original, und das Kopieren erfordert einen Aufruf des Kopierkonstruktors der Klasse.

Für sehr kleine Klassen (z. B. <16 Byte) ohne Kopierkonstruktor ist es wahrscheinlich effizienter, die Wertesyntax zu verwenden, anstatt Referenzen zu übergeben. Aus diesem Grund sehen Sie void foo(double bar) und nicht void foo(const double &var) . Um jedoch keine Mikro-Optimierung von Code vorzunehmen, der nicht von Bedeutung ist, sollten Sie in der Regel alle echten Objekte per Referenz übergeben und nur eingebaute Typen wie int y void * nach Wert.

8voto

Ðаn Punkte 10643

Es gibt eine riesig einen Unterschied, den noch niemand erwähnt hat: die Aufteilung von Objekten. In einigen Fällen können Sie brauchen const& (oder & ), um ein korrektes Verhalten zu erreichen.

Betrachten Sie eine andere Klasse Derived die erbt von Class . Im Client-Code erstellen Sie eine Instanz von Derived die Sie an func() . Wenn Sie func(const Class&) wird die gleiche Instanz weitergegeben. Wie andere gesagt haben, func(Class) eine Kopie erstellen, haben Sie eine neu (temporäre) Instanz von Class (nicht Derived ) in func .

Dieser Unterschied im Verhalten (nicht in der Leistung) kann wichtig sein, wenn func macht wiederum einen Abstrich. Vergleichen Sie die Ergebnisse der Ausführung des folgenden Codes:

#include <typeinfo.h>

struct Class
{
    virtual void Foo() {};
};
class Derived : public Class {};

void f(const Class& value)
{
    printf("f()\n");

    try
    {
        const Derived& d = dynamic_cast<const Derived&>(value);
        printf("dynamic_cast<>\n");
    }
    catch (std::bad_cast)
    {
        fprintf(stderr, "bad_cast\n");
    }
}

void g(Class value)
{
    printf("g()\n");

    try
    {
        const Derived& d = dynamic_cast<const Derived&>(value);
        printf("dynamic_cast<>\n");
    }
    catch (std::bad_cast)
    {
        fprintf(stderr, "bad_cast\n");
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    Derived d;
    f(d);
    g(d);
    return 0;
}

1 Stimmen

+1. Und vor allem bedeutet das Objekt-Slicing, dass der Compiler 莫れ die vom Fragesteller erwartete Optimierung vornehmen (zumindest für Typen, die polymorph sein könnten), denn dann würde das Objekt nicht gesliced werden, obwohl der Standard dies verlangt.

4voto

LiraNuna Punkte 61060

Moderne Compiler werden das sicher tun am effizientesten, indem sie entweder Syntax

Der Compiler kompiliert nicht, was Sie "meinen", sondern was Sie sagen Sie es zu . Compiler sind nur für Optimierungen auf niedrigerer Ebene und für Probleme, die der Programmierer übersieht, intelligent (z. B. Berechnungen innerhalb einer for-Schleife, toter Code usw.).

Im zweiten Beispiel weisen Sie den Compiler an, eine Kopie der Klasse zu erstellen - was er ohne nachzudenken tun wird - auch wenn Sie sie nicht verwenden, denn das ist es, was Sie dem Compiler aufgetragen haben.

Das zweite Beispiel fordert den Compiler ausdrücklich auf, dieselbe Variable zu verwenden - das spart Platz und wertvolle Zyklen (es ist keine Kopie erforderlich). Die const ist für Fehler da - da Class &value geschrieben werden kann (manchmal ist das auch gewünscht).

4voto

Greg Hewgill Punkte 882617

Hier sind die Unterschiede zwischen einigen Parameterdeklarationen:

                           copied  out  modifiable
func(Class value)           Y       N    Y
func(const Class value)     Y       N    N
func(Class &value)          N       Y    Y
func(const Class &value)    N       N    N

wo:

  • kopiert: beim Aufruf der Funktion wird eine Kopie des Eingabeparameters erstellt
  • out: value ist ein "out"-Parameter, was bedeutet, dass Änderungen, die innerhalb von func() vorgenommen werden, nach der Rückkehr der Funktion außerhalb der Funktion sichtbar sind
  • modifizierbar: Wert kann innerhalb von func() geändert werden

So sind die Unterschiede zwischen func(Class value) y func(const Class &value) sind:

  • Die erste erstellt eine Kopie des Eingabeparameters (durch Aufruf des Konstruktors Class copy) und ermöglicht es dem Code innerhalb von func(), die value
  • Der zweite tut no eine Kopie anfertigen, und macht no Code innerhalb von func() ändern können value

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X