Warum use = eine primitive Art in C ++ initialisieren?

stimmen
8

Wo ich arbeite, denken die Leute vor allem, dass Objekte am besten unter Verwendung von C ++ initialisiert - Stil Konstruktion (mit Klammern), während primitive Typen sollten mit dem Operator = initialisiert werden:

std::string strFoo( Foo );
int nBar = 5;

Niemand scheint in der Lage zu sein , zu erklären , warum sie die Dinge auf diese Weise bevorzugen, though. Ich kann sehen , dass std::string = Foo;wäre ineffizient sein , weil es eine zusätzliche Kopie bedeuten würde, aber was falsch ist mit nur dem verbannen =Operator zusammen und Klammern überall verwenden?

Ist es eine gemeinsame Konvention? Was ist der Gedanke dahinter?

Veröffentlicht am 09/12/2008 um 18:52
quelle vom benutzer
In anderen Sprachen...                            


9 antworten

stimmen
17

Initialisieren von Variablen mit dem Operator = oder mit einem Konstruktoraufruf semantisch das gleiche, es ist nur eine Frage des Stils. Ich ziehe den Operator =, da es natürlich liest.

Den = Operator in der Regel keine zusätzliche Kopie erzeugen - es ruft nur die normale Konstruktor. Beachten Sie jedoch, dass mit nicht-primitiven Typen, dies nur für Initialisierungen ist, die zur gleichen Zeit wie die Erklärungen auftreten. Vergleichen:

std::string strFooA("Foo");  // Calls std::string(const char*) constructor
std::string strFoo = "Foo";  // Calls std::string(const char*) constructor
                             // This is a valid (and standard) compiler optimization.

std::string strFoo;  // Calls std::string() default constructor
strFoo = "Foo";      // Calls std::string::operator = (const char*)

Wenn Sie nicht-triviale Standardkonstruktoren haben, kann diese Konstruktion etwas ineffizient sein.

Die C ++ Standard , Abschnitt 8.5, Absatz 14 heißt es :

Im anderen Fall (dh für die verbleibenden Kopie-Initialisierung Fälle), wird eine temporäre erstellt. Benutzerdefinierte Konvertierungssequenzen , die vom Quelltyp zu dem Zieltyp oder eine abgeleiteten Klasse davon aufgezählt werden umwandeln können (13.3.1.4) und die besten sind , durch Überladungsauflösung gewählt (13.3). Die benutzerdefinierte Umwandlung so gewählt wird als den Initialisierer Expression in eine temporären umzuwandeln, dessen Typ ist der Typ von dem Aufruf der benutzerdefinierten Konvertierungsfunktion, mit dem cv-Qualifier des Zieltypen zurückgegeben. Wenn die Konvertierung nicht durchgeführt werden kann oder nicht eindeutig ist , wird die Initialisierung schlecht ausgebildet. Das Objekt initialisiert wird , ist dann aus dem temporären-direct initialisiert gemäß den obigen Regeln. 87 )In bestimmten Fällen ist eine Implementierung erlaubt die temporären zu beseitigen, indem das Objekt direkt zu initialisieren; siehe 12.2.

Ein Teil des Abschnitts 12.2 heißt es:

Auch wenn die Erstellung des temporären Objekts vermieden wird, alle semantischen Einschränkungen müssen beachtet werden, wenn das temporäre Objekt erstellt wurde. [Beispiel: Selbst wenn der Kopierkonstruktor nicht aufgerufen wird, werden alle semantischen Einschränkungen, wie Zugänglichkeit (11), wird zufrieden sein. ]

Beantwortet am 09/12/2008 um 18:57
quelle vom benutzer

stimmen
11

Ich habe einfach das Bedürfnis nach einer anderen dummen litb Post.

string str1 = "foo";

heißt copy-Initialisierung , weil das, was der Compiler tut, wenn es keine Provisorien nicht elide hat, ist:

string str1(string("foo")); 

Überprüfung, dass die neben Konvertierungskonstruktor verwendet implizit ist. Tatsächlich sind alle impliziten Konvertierungen werden durch den Standard in Bezug auf die Kopie der Initialisierung definiert. Es wird gesagt, daß eine implizite Konvertierung vom Typ U T ist gültig, wenn

T t = u; // u of type U

ist gültig.

In constrast,

string str1("foo");

genau das tut , was geschrieben wird, und wird als direkte Initialisierung . Es funktioniert auch mit expliziten Konstrukteuren.

By the way, können Sie eliding von Provisorien deaktivieren, indem -fno-elide-Konstrukteure mit:

-fno-elide-constructors
    The C++ standard allows an implementation to omit creating a temporary which 
    is only used to initialize another object of the same type. Specifying this 
    option disables that optimization, and forces G++ to call the copy constructor 
    in all cases.

Die Norm sagt, es gibt praktisch keinen Unterschied zwischen

T a = u;

und

T a(u);

wenn T und die Art von u sind primitive Typen. So können Sie beide Formen verwenden. Ich denke, dass es nur der Stil es, die Menschen benutzen die erste Form eher als die zweite macht.


Manche Menschen können die erste in irgendeiner Situation verwendet werden, da sie die Erklärung eindeutig zu machen möchten:

T u(v(a));

suchen jemanden migh als Definition einer Variablen , udie eine temporäre eines Typs initialisiert wird unter Verwendung , vdie einen Parameter für den Konstruktor wird aufgerufen a. Aber in der Tat, was der Compiler tut mit , dass ist dies:

T u(v a);

Es schafft eine Funktionsdeklaration , die ein Argument vom Typ nimmt v, und mit einem Parameter aufgerufen a. So können die Leute tun

T u = v(a);

dass eindeutig zu machen, auch wenn sie es getan haben könnte

T u((v(a)));

Auch, weil es nie um Funktionsparameter Klammern, würden die Compiler es als Variablendefinition anstelle einer Funktionsdeklaration lesen :)

Beantwortet am 09/12/2008 um 19:21
quelle vom benutzer

stimmen
4

Es sei denn , Sie haben bewiesen , dass es in Bezug auf Leistung ankommt, würde ich nicht eine zusätzliche Kopie mit dem Zuweisungsoperator in Ihrem Beispiel Sorgen über ( std::string foo = "Foo";). Es würde mich ziemlich überrascht , wenn diese Kopie existiert auch , wenn Sie auf den optimierten Code aussehen, glaube ich , dass die entsprechenden parametrisierte Konstruktor tatsächlich nennen.

In Antwort auf Ihre Frage, ja, ich würde sagen, dass es eine ziemlich gemeinsame Konvention ist. Herkömmlicherweise haben die Menschen Zuordnung verwendeten eingebaute Typen zu initialisieren, und es gibt keine zwingenden Grund, die Tradition zu ändern. Ablesbarkeit und Gewohnheit sind absolut gültige Gründe für diese Konvention gegeben, wie wenig Auswirkungen auf die ultimative Code.

Beantwortet am 09/12/2008 um 18:59
quelle vom benutzer

stimmen
2

Sie werden wahrscheinlich, dass Code, zB

std::string strFoo = "Foo";

Tun eine zusätzliche Kopie und kompiliert auf den gleichen Code (ein Anruf aus einem einzigen Argument Konstruktor) als ein mit Klammern vermeidet.

Auf der anderen Seite gibt es Fälle , in denen man muss Klammern, wie eine Konstruktor Mitglied der Initialisierung Liste verwenden.

Ich denke, die Verwendung von = oder Klammern lokale Variablen zu konstruieren, ist weitgehend eine Frage der persönlichen Wahl.

Beantwortet am 09/12/2008 um 18:57
quelle vom benutzer

stimmen
1

Nun, wer weiß , was sie denken, aber ich ziehe auch die = für primitive Typen, vor allem weil sie keine Objekte sind, und weil das die „üblichen“ Weg , um sie zu initialisieren.

Beantwortet am 09/12/2008 um 18:58
quelle vom benutzer

stimmen
0

Aber dann nur verwirren Sie noch mehr Sie Primitiven in der Initialisierungsliste mit Objektsyntax initialisieren.

foo::foo()   
  ,anInt(0)   
  ,aFloat(0.0)   
{   
}   
Beantwortet am 09/12/2008 um 19:44
quelle vom benutzer

stimmen
0

Ein Argument, man könnte für machen:

std :: string foo ( "bar");

Ist, dass es die Dinge gleichen hält, auch wenn das Argument zählen Änderungen, dh:

std :: string foo ( "bar", 5);

Funktioniert nicht mit einem Zeichen ‚=‘.

Eine andere Sache ist, dass für viele Objekte a ‚=‘ fühlt sich unnatürlich, zum Beispiel sagen, dass Sie eine Array-Klasse, wo das Argument die Länge ergibt:

Array arr = 5;

Fühlt sich nicht gut, da wir nicht über ein Array mit dem Wert 5 konstruieren, aber mit einer Länge von 5:

Array arr (5);

da Sie ein Objekt mit dem angegebenen Parameter fühlt sich natürlicher, bauen, nicht nur einen Wert zu kopieren.

Beantwortet am 09/12/2008 um 19:36
quelle vom benutzer

stimmen
0

Ich glaube, dass eher eine Gewohnheit ist, nur sehr wenige Objekte mit = initialisiert werden können, ist die Zeichenfolge einer von ihnen. Es ist auch eine Art und Weise zu tun, was Sie gesagt haben „Klammer mit überall (dass die Sprache ermöglicht es Ihnen, es zu benutzen)“

Beantwortet am 09/12/2008 um 18:59
quelle vom benutzer

stimmen
0

Es ist eine Frage des Stils. Auch die Aussage, dass „std :: string =‚Foo‘, ineffizient sein würde, weil es eine zusätzliche Kopie bedeuten würde“ ist nicht korrekt. Diese „zusätzliche Kopie“ wird vom Compiler entfernt.

Beantwortet am 09/12/2008 um 18:57
quelle vom benutzer

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more