2010-03-01 22 views
5

Próbowałem następujący kod:Przeciążenie operatora << i rekurencji

#include <iostream> 
using std::cout; 
using std::ostream; 

class X 
{ 
public: 
    friend ostream& operator<<(ostream &os, const X& obj) 
    { 
     cout << "hehe";   // comment this and infinite loop is gone 
     return (os << obj); 
    } 
}; 

int main() 
{ 
    X x; 
    cout << x; 
    return 0; 
} 

Kiedy skompilować & uruchomić ten, to zgodnie z oczekiwaniami; nieskończona pętla. Jeśli usuniemy instrukcję cout wewnątrz funkcji znajomego, rekursja się nie stanie. Dlaczego tak jest?

+0

Używam MinGW (Minimalistyczna wersja GNU dla Windows) btw. – legends2k

+1

Jak można zauważyć, że rekursja się nie dzieje? Czy to właściwie kończy program? Czy po prostu nic nie drukuje i trzeba to zakończyć? (Zauważ, że rekursja ogona może sprawić, że nie otrzymasz przepełnienia stosu). –

+0

@litb: Nieodparcie spadł z powrotem do terminala (nie nacisnąłem ani jednego klucza, żeby zabić proces). – legends2k

Odpowiedz

7

Optymalizator decyduje, że cała pozostała aktywność nie ma wpływu i optymalizuje ją. Niezależnie od tego, czy jest to dobre czy złe, to inna sprawa.

W szczególności:

X x; 

tworzy pusty obiekt "x"

cout << x; 

połączenia:

return (os << obj); 

który jest dołączanie pusty obiekt; kompilator zauważa, że ​​"os" nie rozrósł się od ostatniego połączenia i nie ma obietnicy, że robi to dalej (i nic więcej się nie dzieje), więc decyduje, że cała firma jest zbędna i może zostać obcięta w tym momencie.

W przypadku wywołania

cout << "hehe";   // comment this and infinite loop is gone 

jest jakaś dodatkowa aktywność więc optymalizator nie usuwa następujące połączenia.

Zgaduję, że jeśli zainicjowałeś x z czymkolwiek niepustym lub wykonałeś inną niż zerowa aktywność inną niż cout << "hehe";, powtarzałbyś, że rekursja działa tak samo.

+0

Właściwie uważam, że optymalizator byłby całkowicie trafny, gdyby standard stwierdził, że nieskończona rekursja powoduje "niezdefiniowane zachowanie"; nie robienie niczego byłoby w tym przypadku OK. Nie mogę znaleźć kopii standardu C++ w sieci, czy każdy, kto ma to sprawdzić? –

+0

... w sprawie niepowiązanych uwagę, zastanawiam się, co jeśli połączenie zostało zawarte w 'try-catch (...)' do przechwytywania wyjątek przepełnienia stosu.Co, jak sądzę, byłoby raczej oczekiwanym zachowaniem, popraw mnie, jeśli się mylę. –

+1

Nie sądzę, że taki wyjątek (który, nawiasem mówiąc, nie jest wyjątkiem C++, ale jest to pewien rodzaj sygnału w * NIX i wyjątek SEH w systemie Windows) może zostać przechwycony. Właściwie nic nie możesz zrobić, gdy stos się przepełni: wszystko już oszalało. Jeśli zauważysz, aplikacje systemu Windows, które przechodzą przepełnienie stosu, nie wyświetlają nawet komunikatu o błędzie, ponieważ nie mogą teraz nic zrobić, gdy nie ma już stosu. –

6

W obu przypadkach (z lub bez pisania „hehe”) Visual Studio 2005 daje następujące ostrzeżenie:

warning C4717: 'operator<<' : recursive on all control paths, function will cause runtime stack overflow 

w obu przypadkach kompiluje iw obu przypadkach daje przepełnienie stosu.

Jednak bez "hehe" przepełnienie stosu występuje nieco wcześniej.

+0

Masz rację Patrick, dzięki! I dzięki litbowi za wątpienie w słuszność mojego zerwania, że ​​rekursja się nie dzieje, co sprawiło, że uruchomiłem ją przez 'gdb' i teraz jestem zadowolony widząc' SIGSEGV' który udowodni, że w obu przypadkach występuje rekursja :) – legends2k

Powiązane problemy