2013-06-06 6 views
14

Rozumiem, że kiedy definiujemy konstruktor klasy klasy, klasa jest niezbędna jako stany Rule of three. Ja również zauważyć, że argument konstruktora kopii jest zwykle const jako następujące kody zilustrować:Dlaczego konstruktor kopiowania C++ musi używać obiektu const?

class ABC { 
public: 
    int a; 
    int b; 
    ABC(const ABC &other) 
    { 
     a = other.a; 
     b = other.b; 
    } 
} 

Moje pytanie brzmi: co się stanie, jeśli argument konstruktora kopii nie jest const:

class ABC 
{ 
    public: 
    int a; 
    int b; 
    ABC(ABC &other) 
    { 
     a = other.a; 
     b = other.b; 
    } 
} 

Rozumiem, że w niektórych przypadkach, jeśli argument konstruktora kopiowania jest stały, druga implementacja zakończy się niepowodzeniem. Również jeśli argument konstruktora kopiowania jest stały, obiekt, który ma być skopiowany, nie zmieni jego zawartości podczas procesu. Zauważam jednak, że niektórzy ludzie nadal używają drugiej implementacji, a nie pierwszej. Czy są jakieś powody, dla których preferowana jest druga implementacja?

+4

Dlaczego "A" należy zmodyfikować w 'ABC B (A)'? Ma to mało sensu i byłoby dość nieintuicyjne. – juanchopanza

+8

Prawdopodobnie dlatego, że autor zapomniał zrobić to const. –

+0

Jeśli przejdziemy przez twój tytuł, jest to duplikat: [Dlaczego argument konstruktora kopiowania jest stały] (http://stackoverflow.com/questions/1602058/why-is-the-copy-constructor-argument-const) (Jeśli dojdziemy do końca twojego tekstu, to nie jest). – jogojapan

Odpowiedz

15
  • Logicznie rzecz biorąc, powinno to nie ma sensu, aby zmodyfikować obiekt, którego po prostu chcesz zrobić kopię, choć czasami może to mieć jakiś sens, jak w sytuacji, gdzie chcesz przechowywać czas, przez jaki obiekt został skopiowany. Ale to może działać ze zmienną składową mutable, która przechowuje tę informację i może być modyfikowana nawet dla obiektu const (a drugi punkt będzie uzasadniał to podejście)

  • Chciałbyś móc tworzyć kopię obiektów const . Ale jeśli nie przekazujesz argumentu z kwalifikatorem const, nie możesz tworzyć kopii obiektów const ...

  • Nie można utworzyć kopii z tymczasowego odniesienia, ponieważ tymczasowe obiekty są wartością racjonalną i mogą nie będzie związany z odniesieniem do non const. W celu bardziej szczegółowego wyjaśnienia, sugeruję Herb Sutter's article on the matter

+3

także fakt, że wersja pobierająca odniesienia niestałe nie będzie działać dla tymczasowych. – juanchopanza

+0

@juanchopanza Masz rację, redagowałem, aby dodać to! – JBL

+0

tylko uwaga: nawet odwołanie liczące smartpointów nie wymaga mutables. Licznik referencyjny nie znajduje się w samym smartpointeru, ale zwykle w specjalnym obiekcie pośrednim lub wewnątrz referenta (inwazyjne implementacje). Tak więc oryginalny _smartpointer_ pozostaje niezmieniony i może być zadeklarowany jako const. – user396672

1

Konstruktory kopiowania nie powinny modyfikować kopiowanego obiektu, dlatego preferowane jest ustawienie const w parametrze other. Oba będą działać, ale preferowane jest ustawienie const, ponieważ wyraźnie stwierdza, że ​​obiekt przekazany nie powinien być modyfikowany przez funkcję.

const jest przeznaczony wyłącznie dla użytkownika. Nie istnieje dla rzeczywistego pliku wykonywalnego.

+2

Nie do końca prawda; jeśli obiekt wykonuje np. liczenie referencyjne, wówczas może być konieczna modyfikacja rhs. –

+3

@OliCharlesworth: Twierdzę, że powinien to być element 'mutable', ponieważ zwykle liczba odwołań nie jest częścią obserwowalnego stanu –

+0

@AndyProwl: agreed. Właśnie chciałem rozwiać twierdzenie, że rhs nigdy nie jest modyfikowane;) –

10

Ostatnią rzeczą, jakiej może oczekiwać każdy konsument twojej klasy, jest konstruktor kopii, który zmienił obiekt, który został skopiowany! Dlatego zawsze należy oznaczyć jako const.

2

Jeśli konstruktor kopiowania nie określi tego parametru jako const, ten fragment nie zostanie skompilowany.

const ABC foo; 
ABC bar(foo); 
+0

Albo, być może, jeszcze bardziej przebiegle, nie byłoby to również: 'ABC bar (some_func_returning_ABC());' – Angew

6

Istnieją dwa powody, które mogą być const tu potrzebne:

  1. To gwarantuje, że nie przypadkowo „szkody” oryginalny Dokonując kopię - to dobrze, ponieważ nie chcesz, aby oryginalny obiekt został zmieniony podczas kopiowania!
  2. Możesz przekazać coś innego niż obiekt podstawowy - ponieważ konstruktor przyjmuje odwołanie, jeśli nie jest obiektem - powiedzmy na przykład wyrażenie.

Aby zilustrować ten drugi przypadek:

class ABC 
    { 
     public: 
      int a; 
      int b; 
     ABC(const ABC &other) 
     { 
     a = other.a; 
     b = other.b; 
     } 
     ABC operator+(const ABC &other) 
     { 
      ABC res; 
      res.a = a + other.a; 
      res.b = b + other.b; 
      return res; 
     } 
    } 

    ... 
    ABC A; 
    a.a = 1; 
    a.b = 2; 
    ABC B(a+a); 

Nie będzie to skompilować jeśli konstruktor jest ABC(ABC &other), ponieważ a+a jest tymczasowym obiektem typu ABC. Ale jeśli jest to ABC(const ABC &other), możemy użyć tymczasowego wyniku obliczenia i nadal przekazywać go jako odniesienie.

5

Jak wskazuje kilka innych odpowiedzi, konstruktor kopii, który zmodyfikował swój argument, byłby nieprzyjemną niespodzianką. Nie jest to jednak jedyny problem. Kopiowanie konstruktorów jest czasem używane z argumentami, które są tymczasowe. (Przykład: return from function.) I niestałe odniesienia do temporaries nie latają, jak wyjaśnił elsewhere na SO.

0

To nie jest "musi" w sensie technicznym. Mamy nawet takie bestie w standardzie, choćby it got deprecated. Śledź linki do uzasadnienia.

Oczekujemy, że semantyka kopii pozostawi niezmieniony "szablon" i zapewni klon, który jest dokładnie taki sam pod każdym względem, że trudno jest powiedzieć oryginałowi.

Oczekuje się, że będziesz dwa razy pomyślał, aby mieć ctor, który robi inaczej. Zaskoczy użytkowników i prawdopodobnie wprowadzi błędy. I frustracja i hałas, po prostu spróbuj google dla "wektora auto_ptr" tylko po to, by zobaczyć liczbę.

Pozostała część pytania może brzmieć: "Przysięgam, że nie dotknę oryginału podczas realizacji, ale chcę innego podpisu". Jaki więc podpis? Spróbujmy T i T &.

T znika, ponieważ wymagałoby to użycia kopii, abyśmy mogli ją wykorzystać i właśnie to wdrażamy. Rekursja: patrz rekursja.

To pozostawia T &. To faktycznie działa w przypadku wielu spraw. Ale po prostu nie udaje się, jeśli twój oryginalny obiekt siedzi w formie stałej lub jest tymczasowy. Po co przeszkadzać w tym sensownym przypadku bez żadnego deszczu?

0

Poza podstawowym założeniu, że kopiowanie konstruktorów nie powinien modyfikować instancję źródłowego, ten artykuł omawia rzeczywistej przyczyny techniczne korzystania const:

http://www.geeksforgeeks.org/copy-constructor-argument-const/

Mianowicie, że cytuję:

"... utworzony tymczasowo obiekt nie może być związany z niestanowiącymi wartościami stałymi odniesień ..."