2013-06-11 15 views
15

Dlaczego poniższy program C++ wyświetla "ACCA"? Dlaczego operator int() jest wywoływany dwa razy?Przeciążanie operatora C++

#include "stdafx.h" 
#include <iostream> 

using namespace std; 

class Base { 
public: 
    Base(int m_var=1):i(m_var){ 
     cout<<"A"; 
    } 
    Base(Base& Base){ 
     cout<<"B"; 
     i=Base.i; 
    } 
    operator int() { 
     cout<<"C"; 
     return i; 
    } 
private: 
    int i; 
}; 

int main() 
{ 
    Base obj; 
    obj = obj+obj; 
    return 0; 
} 
+0

FYI; Jest to konstrukcja stworzona do tworzenia ACCA, jako ćwiczenie dla czytelnika. –

Odpowiedz

30

pierwsze, ta linia:

Base obj; 

domyślnych konstrukty obiekt obj przez wybranie konstruktor przyjmuje liczbę całkowitą o wartości domyślnej 1. Jest to odpowiedzialne za wydrukowanie pierwszego numeru A na standardowe wyjście.

Wtedy to wyrażenie:

obj + obj 

Wymaga zbieranie realną przeciążenie operator +. W tym przypadku, ponieważ obj ma zdefiniowaną przez użytkownika konwersję na int, wybierany jest wbudowany operator +, a oba argumenty są konwertowane na int. Jest to odpowiedzialne za drukowanie na standardowym wyjściu dwóch modeli C.

Następnie, przypisanie do obj w:

obj = obj + obj 

potrzebuje skorzystać z domyślnie generowane operator = dla Base. Niejawnie generowane operator = ma podpis:

Base& operator = (Base const&); 

Oznacza to, że wyrażenie po prawej stronie znaku równości, która jest typu int, musi zostać przekształcona w tymczasowym Base obiektu, z którego obj jest przypisany (referencja parametr niejawnie wygenerowanego operator = jest związany z tym tymczasowym).

Jednak tworzenie tego tymczasowego z int kolei wymaga wywoływania konwersji budowę Base że akceptuje int Ponownie, który jest odpowiedzialny za drugim A drukowanego do standardowej produkcji.

9

operator int() nazywa się dwa razy, ponieważ nie przeciążony operator+. Kompilator nie wie, jak dodać Base do Base, więc są one konwertowane na int (ponieważ nauczyłeś go, jak to zrobić), które wie, jak to zrobić. Następujące drukuje kod ADA:

#include <iostream> 

using namespace std; 

class Base { 
public: 
    Base(int m_var=1):i(m_var){ 
     cout<<"A"; 
    } 
    Base(Base& Base){ 
     cout<<"B"; 
     i=Base.i; 
    } 
    operator int() { 
     cout<<"C"; 
     return i; 
    } 
    int operator+(Base& Base) 
    { 
     cout<<"D"; 
     return i+Base.i; 
    } 
private: 
    int i; 
}; 

int main() 
{ 
    Base obj; 
    obj = obj+obj; 
    return 0; 
} 
1

odnieść się do obj dwukrotnie ekspresji obj+obj a każda taka informacja musi być przekształcony w całkowitej.

To nie jest niemożliwe (chociaż jest to okropny pomysł), że taka konwersja może być "stanowa" - to znaczy, że poprzez projektowanie może zwrócić inną wartość za każdym razem, gdy zostanie wywołana. W końcu wartość reprezentowana przez obj może się zmienić (może to być licznik lub coś w tym stylu). Tak więc kompilator musi to ocenić na nowo dla każdego odwołania.

+0

Ponieważ 'operator int()' nie jest oznaczony 'const', sygnatura mówi bezpośrednio kompilatorowi, że powinien oczekiwać połączenia w celu zmodyfikowania stanu obiektu. Jednak nawet dla operatora typu const, drugie wywołanie nie będzie mogło zostać zoptymalizowane dokładnie dlatego, że może być w nim coś takiego jak "cout" (a tutaj rzeczywiście jest). – celtschk

+0

I możemy myśleć o funkcji, która ma efekt uboczny (jak pisanie do standardowego wyjścia) jako zwracanie nowej wersji wszechświata za każdym razem, gdy jest wywoływana, co jest innym sposobem interpretowania "zwracania innej wartości za każdym razem, gdy jest nazywa". –

1

To wywołanie raz na operand, nie ma znaczenia, czy jest to to samo wystąpienie.

obj = obj+obj; 

"rzuca" pierwszy operand na int, a następnie na drugi.

Musisz przeciążyć operatora +, jeśli chcesz uniknąć tego niejawnego rzutu.

1

Wywołuje dwa razy operator int(), ponieważ musi przekonwertować oba obiekty na int, zanim będzie mógł je dodać.

5

Podczas budowy obiektu, pojawi się pierwszy „A”:

Base obj; 

Po określeniu dodać obj+obj, kompilator musi wymyślić sposób na wykorzystanie + na obj. Ponieważ nie przesłaniać operator+ dla Base, konwersja do int() jest wywoływana dla każdej strony równania:

obj+obj 

Drukuje "CC".

Następnie należy przypisać do obj, która jest typu Base, więc konstruktor, który może przyjąć int (i + i od operatora int()) jest prowadzony, która drukuje "A":

obj = obj+obj; // Assignment prints "A" 
5
obj = obj+obj; 
     ^^^--------obj converted to int here 
      ^^^----obj converted to int here 
^^^^^------------Base(int) ctor and default operator= called here 

Przeciążenie operatorów lane generalnie nie jest dobrym pomysłem, jeśli nie zrozumieć koszty i wiem na fakt, że korzyści w swoim konkretnym przypadku przewyższają je.

1

Pierwszy pochodzi z

Base obj;

Dwa Cs pochodzę konwersji OBJ w obj+obj do int jak nie przeciążać operator +.

Ostatni A pochodzi z konwersji obj = powstałego int do obj.

2

W jaki sposób program C++ oblicza wartość "ACCA"?

Pierwsza wyświetlana litera to "A". To wyjście jest powiązane z tą linią:

Base obj; 

... w której tworzysz nową instancję Base.

Następny wiersz jest nieco skomplikowana:

obj = obj+obj; 

Normalnie, to jest tłumaczona na obj.operator+(obj), ale nie masz operator + przeciążone w klasie Base, więc to tłumaczenie nie jest ważna. Pozostała możliwość jest taka, że ​​operator + jest rzeczywiście operatorem dodawania liczb.

I tak, jest to możliwe, ponieważ podałeś obsadę do int. Tak więc możliwe jest przekształcenie każdego członu równania w int ... i dlatego operator int jest wywoływana dwa razy. Faktyczna liczba wywołań operator int zależy od aktywowanych optymalizacji. Na przykład kompilator może zdawać sobie sprawę, że oba terminy są takie same, a następnie utworzyć nowy tymczasowy, gdy operator int został wywołany po raz pierwszy. W takim przypadku zobaczysz CA zamiast CC.

Na koniec wykonywane jest wyrażenie przypisania obj.operator=(temp). Tutaj słowo kluczowe to temp. Aby zadziałał defaultoperator=, ponieważ nie jest on przeciążony, musisz mieć obiekt Base po prawej stronie. W rzeczywistości jest to możliwe, ponieważ Base używa int do budowania nowych instancji. Ok, więc wynikiem obj + obj był int (powiedzmy, że nazywa się „x”) liczba i kompilator tworzy tymczasowy obiekt klasy Base, która jest zbudowana z numerem x, jak następujący wiersz został stracony:

Base temp(x); 

W ten sposób ostatnia litera jest "A". Znowu, wiele kompilatorów może w niektórych przypadkach unikać budowania tymczasowych, więc na końcu nie można zobaczyć litery "A".

pamiętać, że ta linia:

obj = obj + obj 

było zatem rozkładowi w:

int x = ((int) obj) + ((int) obj); 
Base temp(x); 
obj = temp; 

dyspozycja końcowym ma wynik jakby pamięć, w której obj siedzi będą zajęte z treścią temp (to rola domyślnego konstruktora kopiowania, który wykonuje operator= dla każdego członka klasy, zobacz ponownie "regułę trzech").

Przeciążenie operatora wiąże się z wieloma problemami, które być może nie są przewidziane, jeśli nie masz większej lub mniejszej znajomości języka, jak widać. Weź również pod uwagę, że języki takie jak Java całkowicie uniemożliwiają jego użycie, podczas gdy C# pozwala na to z kontrolowanej perspektywy.

Powiązane problemy