2009-09-25 14 views
12

Mam klasę, która wymaga innego niż domyślny konstruktora kopiowania i operatora przypisania (zawiera listę wskaźników). Czy istnieje jakiś ogólny sposób ograniczenia duplikacji kodu między konstruktorem kopiowania a operatorem przypisania?Zmniejszenie duplikacji kodu między operatorem = a konstruktorem kopiującym

+2

Myślę, że powinieneś być w stanie wywołać konstruktora kopii wewnątrz operatora = –

+0

Niektóre odpowiedzi są tutaj: http://stackoverflow.com/questions/1457842/is-this-good-code-copy-ctor-operator –

Odpowiedz

16

Nie ma „ogólny sposób” do pisania własnych konstruktorów kopiowania i operatorów przypisania, który działa we wszystkich przypadkach. Ale istnieje idiom nazywa „kopiowaniem & -swap”:

class myclass 
{ 
    ... 
public: 
    myclass(myclass const&); 

    void swap(myclass & with); 

    myclass& operator=(myclass copy) { 
     this->swap(copy); 
     return *this; 
    } 

    ... 
}; 

Jest to przydatne w wielu (ale nie wszystkich) sytuacje. Czasami możesz zrobić lepiej. Wektor lub ciąg może mieć lepsze przypisanie, które ponownie wykorzysta przydzielony obszar pamięci, jeśli jest wystarczająco duży.

+2

+1 - dobre i lakoniczne podsumowanie wymiany kopii myślę. Dobry pomysł na ponowne wykorzystanie pamięci. –

+2

Możesz wskazać subtelność tutaj pomiędzy operatorem = i bardziej standardowym myclass & operator = (const myclass i inne); – Bill

+1

Funkcja swap() powinna być prawdopodobnie oznaczona jako nothrow. Komentarz Ditto on Bills. Jeszcze lepiej byłoby wyjaśnić. –

15

Wyróżnij wspólny kod funkcji prywatnego członka. Prosty (raczej wymyślony) przykład:

#include <iostream> 

class Test 
{ 
public: 
    Test(const char* n) 
    { 
    name = new char[20]; 
    strcpy(name, n); 
    } 

    ~Test() 
    { 
    delete[] name; 
    } 

    // Copy constructor 
    Test(const Test& t) 
    { 
    std::cout << "In copy constructor.\n"; 
    MakeDeepCopy(t); 
    } 

    // Assignment operator 
    const Test& operator=(const Test& t) 
    { 
    std::cout << "In assignment operator.\n"; 
    MakeDeepCopy(t); 
    } 

    const char* get_name() const { return name; } 

private: 
    // Common function where the actual copying happens. 
    void MakeDeepCopy(const Test& t) 
    {   
    strcpy(name, t.name); 
    } 

private: 
    char* name; 
}; 

int 
main() 
{ 
    Test t("vijay"); 
    Test t2(t); // Calls copy constructor. 
    Test t3(""); 
    t3 = t2; // Calls the assignment operator. 

    std::cout << t.get_name() << ", " << t2.get_name() << ", " << t3.get_name() << '\n'; 

    return 0; 
} 
+1

+1 - to prawie zawsze najlepszy sposób na powielenie kodu w obrębie klasy. –

+3

wyciek pamięci! MakeDeepCopy ignoruje możliwość nazwy już wskazującej na przydzieloną pamięć. – sellibitze

+4

Szkoda, że ​​konstruktor kopiowania nie może korzystać z inicjalizatorów elementów, w ten sposób ... – xtofl

6
My &My::operator = (My temp) // thanks, sellibitze 
{ 
    swap (*this, temp); 
    return *this; 
} 

i wdrożenie specjalistycznego std::swap<> (My &, My &).

+1

Oto jak powinien wyglądać plik copy - & - swap. Tworzysz wyraźnie kopię, która w innym przypadku może zostać usunięta. Tutaj jest świetny artykuł: http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/ – sellibitze

+3

Jak zauważa Dave, alexandrescu o tym około 6 lat temu: http : //www.ddj.com/cpp/184403855. Ciekawa lektura! –

+1

errm, Nawiasem mówiąc plakat jest również nazywany Dave. Przykro mi z powodu zamieszania - miałem na myśli Dave'a Abrahamsa z cpp-next :) –

2

Jak już zostało wskazane przez kilka plakatów, posiadanie operatora = utworzenie nowego obiektu z konstruktorem kopiującym, a następnie użycie swap jest powszechnie stosowaną techniką, która nie musi replikować kodu w operatora =.

To powiedziawszy, chcę wskazać profesjonalistę i oszustę tej techniki, która pomoże ci zdecydować, czy jest to właściwe.

Pro - wyjątek bezpieczeństwa

Jeśli obiekt ma wymagań dotyczących zasobów, które mogą spowodować rzut i zakładając, że wymiany nie rzuci, technika ta stanowi silną gwarancję bezpieczeństwa wyjątków (albo obiekt jest przypisany do nabrała wartość innego obiektu lub jest niezmieniona).

Con - zasób ślad

Problem z tej techniki jest to, że wymaga to zupełnie nowy obiekt, który ma zostać utworzony przed stary zostanie zwolniony. Jeśli twój obiekt wymaga dużej ilości zasobów, może to stanowić problem.