2016-04-17 12 views
1

Was ist optimal in diesem Code?Kopieren oder Referenz im Konstruktor?

class C 
{ 
public: 
    C(const std::string& str): str_(str) {} 
private: 
    std::string str_; 
} 

Oder

class C 
{ 
public: 
    C(std::string str): str_(str) {} 
private: 
    std::string str_; 
} 

Bin ich richtig, dass in dem zweiten Code, den ich habe zwei Kopieren und ich habe eine in dem ersten Code? Also zuerst ist besser?

Aber manchmal würde Ich mag C-Klasse wie erstellen:

C c(std::string()); 

Und ich habe eine Warnung (GCC), aber Code ist in Ordnung, in diesem Fall ...

+1

Es ist besser, die Unveränderbarkeit anzugeben. (Aber da dies ein Konstruktor ist, ist es sowieso ziemlich offensichtlich.) –

+2

Ich denke, Variante 2, wenn Sie 'str_ (str)' in 'str_ (std :: move (str))' auch eine Kopie im schlimmsten Fall, und 0 Kopien, wenn Sie std :: string in 'C' Objekt verschieben und nicht mehr verwenden. Außerdem müssen Sie nicht zwei Konstruktoren erstellen, sondern eine Kopie für die andere, so dass Variante 2 in 'C++ 11' besser ist – fghj

+0

nur zwei Konstruktoren zum Kopieren (' C (const std :: string &) ') und Verschieben ('C (std :: string &&)'). Daher wählt der Compiler den Konstruktor abhängig vom Kontext aus, wenn man bedenkt, dass man nicht von lvalue weggehen sollte (weil es ihn korrumpieren würde), und es keinen Grund gibt, von rvalue zu kopieren. Gleiches gilt für 'operator =' –

Antwort

1

Ich denke, Variante 2 in c++11 Tage besser ist, weil:

#include <iostream> 

#define HI do { std::cout << __PRETTY_FUNCTION__ << "\n"; } while (false) 

class String { 
public: 
    String() { HI; } 
    ~String() { HI; } 
    String(const String &) { HI; } 
    String(String &&) { HI; } 
    String& operator=(const String &) { HI; return *this; } 
    String& operator=(String &&) { HI; return *this; } 
}; 

#if 1 
class C 
{ 
public: 
    C(const String &str): str_(str) {} 
private: 
    String str_; 
}; 
#else 
class C 
{ 
public: 
    C(String str): str_(std::move(str)) {} 
private: 
    String str_; 
}; 
#endif 

int main() 
{ 
    C c{String()}; 
    std::cout << "2\n"; 
    String str; 
    C c2{str}; 
    std::cout << "3\n"; 
    C c3{std::move(str)}; 
} 

, wenn Sie die Variante 1 können Sie erhalten: copy + + kopieren,

und in Variante 2 erhalten Sie: Umzug + Kopieren + Bewegen + zwei Züge.

+0

keine Ihrer 'Klasse C' Implementierungen enthält' const String & 'Konstruktor. Das verursacht eine Kopie ohne zusätzliche Züge –

+0

@AndreiR. Ja, Kopieren/Einfügen falsche Variante – fghj

+0

noch eine Sache zu beheben: "Wenn Sie Variante 1 aktivieren, erhalten Sie: Kopieren + Kopieren + Kopieren" -> "Sie erhalten eine Kopie" –

2

Warum nicht einfach hat eine Standardparameterwert im Konstruktor

class C 
{ 
public: 
    C(const std::string& str = ""): str_(str) {} 
          // ^^^^ 
private: 
    std::string str_; 
}; 

und schreiben

C c; 

statt

C c(std::string()); 

?


Beachten Sie, dass die

C(const std::string& str = "") 

ist ohnehin überflüssig, wenn ein

C() = default; 

Konstruktor deklarieren.


Bin ich das richtig in dem zweiten Code habe ich zwei Kopieren und ich habe eine in dem ersten Code?

Da std::string unterstützt den Umzug Konstruktor std::string(std::string&& s) Sie müssen in der Regel nicht über das für diesen Fall zu kümmern. Der Compiler wird dies bei Bedarf optimieren.

+0

Es gibt auch 'C c (" "); wenn OP explizit sein soll. –

+0

@ πάντα ῥεῖ OK, du hast Recht. Aber wenn ich versuche, 'c' wie folgt zu initialisieren:' C c ("irgendeine Zeichenfolge"); 'Ich habe das gleiche Problem (Warnung). – peter55555

+1

@ peter55555 Unnötiges Kopieren ist unwahrscheinlich, da 'std :: string' bereits 'std :: move()' unterstützt. –

1

C c(std::string()); ist eine Funktionsdeklaration. Ihre Warnung bezieht sich wahrscheinlich darauf.

Ihre zwei Codebeispiele lassen die gleiche Syntax zum Erstellen eines Objekts zu. Sie könnten die Funktionsdeklaration Problem vermeiden, indem eine dieser mit:

C c = std::string(); 
C d{ std::string() }; 
C e(""); 

und/oder, wie andere vorgeschlagen haben, ein Standardargument zu machen.


In Bezug auf, ob 1 oder 2 ist besser: 2 sollte str_(std::move(str)) geändert werden. Ansonsten macht str_(str) eine unnötige Kopie.

Theoretisch wäre Option 2 schneller für die Initialisierung von einem rvalue, das eine lange Zeichenfolge enthält, obwohl Sie wahrscheinlich keinen Unterschied bemerken werden.

0

In den meisten Fällen (wenn die Initialisierungszeichenfolge bereits erstellt und verwendet wurde), ist die erste Variante besser (wegen der fehlenden std :: move in der zweiten). Aber jetzt, in C++ 11 Tage, die beste Lösung ist natürlich alle möglichen Situationen abdecken:

C() = default; 
C(const std::string & str): str_{str} {} 
C(std::string && str): str_{std::move(str)} {} 
C & operator= (const std::string & str) { str_ = str; } 
C & operator= (std::string && str): { str = std::move(str); } 

Oder, falls Sie wissen, was machst du da, und Sie brauchen nicht die Daten in der Initialisierung Zeichenfolge Sie können den Inhalt jetzt explizit verschieben:

C(std::string & str): str_{std::move(str)} {} 
Verwandte Themen