2010-07-05 10 views
19

Chcę się upewnić, że rozumiem poprawnie wartość przekazywaną w stosunku do przekazywanej referencji. W szczególności zajmuję się przedrostkami/postfiksami operatora przyrostowego ++ dla obiektu.Operatory przyrostków prefiks/Postfiks

Załóżmy mamy następujące klasy X:

class X{ 
private: 
    int i; 
public: 
X(){i=0;} 
X& operator ++(){ ++i; return *this; } //prefix increment 

X operator ++ (int unused){ //postfix increment 
    X ret(*this); 
    i++; 
    return ret; 
} 

operator int(){ return i; } //int cast 
}; 

Przede wszystkim, ja wdrożył prefiksu/operatorów Postfix przyrostu prawidłowo?

Po drugie, jak wydajna pamięć jest operatorem przyrostowym, w porównaniu do operatora prefiksu? W szczególności, ile kopii obiektów obiektu X jest tworzonych, gdy używana jest każda wersja operatora?

Dokładne wyjaśnienie tego, co dzieje się z funkcją powrotu do referencji a funkcją powrotu do wartości, może mi pomóc w zrozumieniu.


Edit: Na przykład za pomocą następującego kodu ...

X a; 
X b=a++; 

... A i B są teraz aliasy?

+0

Nie ma potrzeby zwiększania przyrostowego "i" w operatorze przyrostkowym. W rzeczywistości robiłbym to, co [FredOverflow sugeruje] (http://stackoverflow.com/questions/3181211/3181359#3181359) i wywoływał wersję prefiksu. IMO jest rzeczywiście bardziej idiomatyczna niż ponowne wdrażanie inkrementacji (nawet jeśli implementacja jest tu banalna). _ I pozbądź się tego niejawnego operatora konwersji. To będzie cię bolało inaczej. (Trzeci i ostatni raz, kiedy napisałem niejawnego operatora konwersji, był w 2001 r., A rok czy dwa lata później odkryłem, że spowodował on subtelne błędy i usunął je - jak wszystkie wcześniej. BTDTGTLS.) – sbi

Odpowiedz

17

To jest poprawna realizacja. Jest typowe, że operator postfiksu będzie gorszy pod względem wydajności, ponieważ przed zrobieniem inkrementu musisz utworzyć kolejną kopię (i dlatego mam zwyczaj używania zawsze prefiksów, chyba że potrzebuję czegoś innego).

Przy pomocy polecenia return-by-reference zwracane jest odwołanie do l-wartości do bieżącego obiektu. Kompilator zazwyczaj realizuje to przez zwracanie adresu bieżącego obiektu. Oznacza to, że zwrócenie obiektu jest tak proste jak zwrócenie numeru.

Jednak z wartością zwracaną należy wykonać kopię. Oznacza to, że jest więcej informacji do skopiowania podczas powrotu (zamiast tylko adresu), jak również konstruktora kopiowania do połączenia. Tutaj pojawia się twoje trafienie wydajnościowe.

Wydajność twojej implementacji wygląda na równi z typowymi implementacjami.

EDYCJA: W odniesieniu do Twojego załącznika, nie, nie są one aliasami. Utworzono dwa oddzielne obiekty. Po zwróceniu przez wartość (i po utworzeniu nowego obiektu z poziomu operatora przyrostowego przyrostka) ten nowy obiekt zostanie umieszczony w odrębnej lokalizacji pamięci.

Jednak w poniższym kodu A i B aliasy:

int a = 0; 
int& b = ++a; 

B jest adresem, który odwołuje się.

+2

Prawidłowo ogólnie, modulo możliwe Zwracana wartość Optymalizacja (http://en.wikipedia.org/wiki/Return_value_optimization). –

2

Twoje operatory są poprawnie zaimplementowane.

W operatorze prefiksu nie są wykonywane żadne kopie X.

W operatorze przyrostkowym jedna kopia jest tworzona dla ret, a potencjalnie inna kopia jest tworzona po powrocie z funkcji, ale wszystkie kompilatory usuwają tę kopię.

15

Bardziej idiomatycznym do wywołania przyrostu prefiksu tego samego obiektu do przyrostu przyrostkiem:

X operator++(int) 
{ 
    X copy(*this); 
    ++*this;   // call the prefix increment 
    return copy; 
} 

Logika zwiększając obiektu X jest zatem wyłącznie zawarty wewnątrz wersji prefiks.

+0

Tak, to uwalnia mnie od opublikowania tej samej poprawki. '+ 1' ode mnie. – sbi