2010-01-27 20 views
29

Ich las Copy and Swap.Was ist Kopie Elision und wie optimiert es die Kopie-und-Austausch-Idiom?

Ich habe versucht, einige Links auf Copy Elision zu lesen, konnte aber nicht richtig herausfinden, was es bedeutete. Kann jemand bitte erklären, was diese Optimierung ist, und vor allem, was durch den folgenden Text gemeint ist

Dies ist nicht nur eine Frage der Bequemlichkeit, sondern in der Tat eine Optimierung. Wenn der/die Parameter an einen L-Wert (ein anderes nicht-konstantes Objekt) gebunden sind, wird beim Erstellen der Parameter automatisch eine Kopie des Objekts erstellt. Wenn s jedoch an einen rvalue (temporäres Objekt, Literal) gebunden ist, wird die Kopie normalerweise entfernt, wodurch ein Aufruf an einen Kopierkonstruktor und einen Destruktor gespeichert wird. In der früheren Version des Zuweisungsoperators, bei der der Parameter als const-Referenz akzeptiert wird, findet die Kopie nicht statt, wenn die Referenz an einen rvalue bindet. Dies führt dazu, dass ein zusätzliches Objekt erstellt und zerstört wird.

+0

Related: [Was ist Kopie elision?] (Http://stackoverflow.com/questions/12953127/what-are-copy-elision-and-return-value-optimization) –

Antwort

33

Der Kopierkonstruktor existiert, um Kopien zu erstellen. In der Theorie, wenn Sie schreiben eine Zeile wie:

CLASS c(foo()); 

Der Compiler die Rückkehr von foo() in c kopieren würde die Kopie Konstruktor aufzurufen.

Kopieren Elision ist eine Technik, die den Aufruf des Kopierkonstruktors überspringt, um den Overhead nicht zu bezahlen. Der Compiler kann beispielsweise veranlassen, dass foo() seinen Rückgabewert direkt in c einrichtet.

Hier ist ein weiteres Beispiel. Angenommen, Sie haben eine Funktion haben:

void doit(CLASS c); 

Wenn Sie es mit einem tatsächlichen Argument nennen, hat der Compiler den Copykonstruktor aufzurufen, so dass die ursprünglichen Parameter nicht geändert werden können:

CLASS c1; 
doit(c1); 

Aber jetzt betrachten ein anderes Beispiel, sagen wir, Sie Ihre Funktion wie folgt aufrufen:

doit(c1 + c1); 

operator+ wird ein temporäres Objekt (ein R-Wert) haben, zu erstellen. Anstatt den Kopierkonstruktor vor dem Aufruf von doit() aufzurufen, kann der Compiler das von operator+ erstellte Temporary übergeben und stattdessen an doit() übergeben. Hier

2

ein Beispiel:

#include <vector> 
#include <climits> 

class BigCounter { 
public: 
    BigCounter &operator =(BigCounter b) { 
     swap(b); 
     return *this; 
    } 

    BigCounter next() const; 

    void swap(BigCounter &b) { 
     vals_.swap(b); 
    } 

private: 
    typedef ::std::vector<unsigned int> valvec_t; 
    valvec_t vals_; 
}; 

BigCounter BigCounter::next() const 
{ 
    BigCounter newcounter(*this); 
    unsigned int carry = 1; 
    for (valvec_t::iterator i = newcounter.vals_.begin(); 
     carry > 0 && i != newcounter.vals_.end(); 
     ++i) 
    { 
     if (*i <= (UINT_MAX - carry)) { 
     *i += carry; 
     } else { 
     *i += carry; 
     carry = 1; 
     } 
    } 
    if (carry > 0) { 
     newcounter.vals_.push_back(carry); 
    } 
    return newcounter; 
} 

void someFunction() 
{ 
    BigCounter loopcount; 
    while (true) { 
     loopcount = loopcount.next(); 
    } 
} 

In somefunction die Linie loopcount = loopcount.next(); profitiert von Kopie elision. Wenn die Kopie nicht erlaubt wäre, würde diese Zeile 3 Aufrufe des Kopierkonstruktors und einen zugehörigen Aufruf an einen Destruktor erfordern. Wenn die Kopierfreigabe erlaubt ist, kann sie auf 1 Aufruf des Kopierkonstruktors reduziert werden, der explizite innerhalb von BigCount::next(), wobei newcounter deklariert ist.

Wenn operator = worden war, wie dies erklärt und definiert:

BigCounter &BigCounter::operator =(const BigCounter &b) { 
    BigCounter tmp(b); 
    swap(tmp); 
    return *this; 
} 

würde es habe 2 Anrufungen des Copykonstruktor haben, auch mit Kopie elision. Eine zum Konstruieren newcounter und die andere zum Konstruieren tmp. Und ohne Kopie Elision wäre es immer noch 3. Deshalb, operator = zu deklarieren, so dass das Argument Aufruf des Kopierkonstrukts kann eine Optimierung sein, wenn die 'Kopieren und Tauschen' Idiom für den Zuweisungsoperator verwenden. Wenn der Kopierkonstruktor zum Konstruieren eines Arguments aufgerufen wird, wird sein Aufruf möglicherweise weggelassen, aber wenn er zum Erstellen einer lokalen Variablen aufgerufen wird, ist dies möglicherweise nicht der Fall.