2012-06-21 21 views
7

Jeśli weźmiemy pod uwagę realizację std :: string, który używa liczenia odniesienia, należy rozważyć następujący scenariusz:Iteratory i odniesienie liczone ciągi

int main() 
{ 
    string english = "Hello"; 
    string german = english; //refcnt = 2 
    string german2 = german; 

    /* L1 */ german[1] = 'a'; 
    /* L2 */ *(german2.begin() + 1) = 'A'; 

    cout << english << endl << german << endl << german2 << endl; 
    return 0; 
} 

co dzieje się w L1 i L2? Czy liczenie odwołań jest zepsute i wykonywana jest głęboka kopia? Myślę, że tak, ale moje obawy mówi, że jeśli to nastąpi, wykonując proste:

cout << german[1] << endl; 

lub prosty:

cout << *(german.begin()) << endl; 

w kontekstach const będzie wykonywać niepotrzebnych głębokie kopie. Czy mam rację? W jaki sposób implementacje radzą sobie z tymi szczegółami?

+0

To jeden z powodów, dlaczego odniesienia liczone 'std :: string's nie są tak popularne. To nie działa w ogóle tak dobrze, jak początkowo wyobrażałem. –

Odpowiedz

6

Masz rację, kopia byłaby wykonana we wszystkich czterech przykładach (L1, L2 i dwóch poniżej), mimo że dla dwóch ostatnich jest to niepotrzebne.

Niestety, gdy wywoływana jest niestanowa wersja operatora [] lub iterator niestanowiący stałej, nie ma sposobu, aby implementacja mogła stwierdzić, czy wynikowe odwołanie nie będzie używane do modyfikowania obiekt, więc musi go bezpiecznie odtworzyć i zrobić kopię.

C++ 11 dodano funkcje cbegin() i cend() do ciągów i innych kontenerów, które zwracają constizatory, nawet jeśli są wywoływane na obiektach niestałych. Pomaga to złagodzić problem. Nie znam porównywalnego rozwiązania dla operatora [].

Uwaga: posiadanie operatora [] lub operatora iteratora *() zwraca typ proxy, jak sugerowali niektórzy z pozostałych respondentów, ponieważ nie jest to opcja, ponieważ powoduje ona przerwanie wymagań kontenera, z których jednym jest fakt, że funkcje te zwracają referencje. (Właśnie dlatego wszyscy zgadzają się, że błędnie jest vector<bool> - w ten sposób korzysta się z serwerów proxy).

(Oczywiście, jeśli piszesz własny odniesienia liczone klasę, nie ma nic Cię powstrzymuje od korzystania typów serwerów proxy, aby to osiągnąć.)

2

Jednym ze sposobów osiągnięcia tego celu są klasy pośredniczące. Więc kiedy indeksujesz w ciągu znaków, otrzymując znak, dostajesz obiekt, który wygląda i wygląda jak znak. Kiedy wykonywany jest zapis, powoduje to głęboką kopię oryginalnego napisu.