2010-02-27 14 views
5

Czytam efektywne C++ w punkcie 5, wspomniałem o dwóch przypadkach, w których muszę samodzielnie zdefiniować operatora przypisania kopii. Przypadek jest klasą zawierającą elementy const i reference.Kiedy powinienem zdefiniować swojego własnego operatora i operatora przypisania

Piszę, aby zapytać, jaka jest ogólna zasada lub przypadek, w którym muszę zdefiniować własnego konstruktora kopii i operatora przypisania?

Chciałbym również wiedzieć, kiedy muszę zdefiniować własny konstruktor i destruktor.

Dziękuję bardzo!

+0

O ile rozumiem (i działam) - dodatkowa dobra praktyka (aby nie wycofywać innych odpowiedzi tutaj) będzie następująca "Zasada trzech": https://stackoverflow.com/q/4172722/1971003 –

Odpowiedz

5

Musisz utworzyć własny konstruktor kopiujący i przypisania operatora (i zazwyczaj konstruktora domyślnego też), gdy:

  • Chcesz, aby obiekt do skopiowania lub przypisany, lub umieścić do standardowego kontenera takich jak vector
  • Domyślny konstruktor kopiowania i operator przypisania nie wykona właściwego zadania.

Rozważmy następujący kod:

class A; // defined elsewhere 
class B { 
private: 
    A *my_very_own_a; 
}; 

Jeśli pozwolisz automatyczne konstruktor kopia kopiować B będzie skopiować wartość A * wskaźnik w poprzek, tak że punkty kopię do samego A przykład jako oryginalny. Ale częścią projektu tej klasy jest to, że każdy B ma swój własny A, więc konstruktor automatycznego kopiowania złamał tę umowę. Musisz więc napisać własny konstruktor kopii, który utworzy nowy A, na którym będzie wskazywać nowy B.

jednak rozważyć tę sprawę:

class A; // defined elsewhere 
class B { 
private: 
    A *shared_reference_to_a; 
}; 

Tutaj każdy B zawiera wskaźnik do A, ale umowa klasa nie wymaga unikalny A dla każdego B. Tak więc automatyczny konstruktor kopiowania może dobrze wykonać właściwą rzecz.

Należy pamiętać, że oba przykłady to ten sam kod, ale z inną intencją projektu .

Przykładem pierwszej sytuacji może być B == okno dialogowe, A == przycisk. Jeśli utworzysz kopię okna dialogowego, prawdopodobnie będzie potrzebna również kopia całej zawartości.

Przykładem drugiego może być B == okno dialogowe, == odniesienie do menedżera okien. Jeśli skopiujesz okno dialogowe, kopia prawdopodobnie istnieje w tym samym menedżerze okien co oryginał.

3

Domyślny konstruktor kopiowania często powoduje, że dwa obiekty "wskazują" wspólny element pamięci. Dzieje się tak dlatego, że domyślnym konstruktorem kopiowania jest przypisanie przynależne. Więc jeśli masz człon wskaźnika, to może Ci w kłopoty

CClass::CClass(const CClass& src) 
    { 
     // these two point to the same place in memory now 
     // you really aught to allocate new memory 
     m_ptr = src.m_ptr; 

     // not a big deal for members that aren't "pointing" elsewhere 
     // this copy works great 
     m_int = src.m_int; 
    } 

można zasadniczo nie chcę skończyć w sytuacji, gdy dwa przypadki są „wskazując na” to samo miejsce w pamięci. Może się to zdarzyć, gdy masz wskaźniki będące członkami, które wskazują dynamicznie przydzieloną pamięć. Może się to również zdarzyć w przypadku odniesień, zarówno odnoszących się do czegoś poza klasą. Może się to zdarzyć w przypadku uchwytów plików, w których może być konieczne duplikowanie/ponowne otwieranie nowego pliku.

W rzeczywistości ten drugi przypadek jest prawdopodobnie najłatwiejszy do konceptualizacji. Udawaj, że twoje 2 obiekty w C++ miały plik dziennika. Bez prawidłowo działającego konstruktora kopiowania, mogliby logować się do tego samego pliku. Co się stanie, jeśli w destruktorze klas plik zostanie zamknięty? Cóż, drugi obiekt C++ będzie miał plik logu zamknięty pod nim. Jednym z rozwiązań byłoby utworzenie konstruktora kopii, który tworzy nowy plik dla nowo skonstruowanej instancji. Tak więc, gdy pierwszy obiekt C++ zniknie, nie zajmie się nim plik logu drugiego obiektu C++. Mają 2 niezależne pliki dziennika, nie mogą się ze sobą ingerować.

Ten przykład można rozszerzyć do pamięci dynamicznej. Bez konstruktora kopiowania, będziesz miał tylko 2 wskaźniki wskazujące na ten sam adres. Jeśli destruktor jednego usuwa tę pamięć, drugi może nie zdawać sobie sprawy, że pamięć, na którą wskazuje, zniknęła. Równie ważne, prawdopodobnie nie chcesz, aby jeden obiekt zapisywał się do elementu innego obiektu :).

Powiązane problemy