2013-02-13 10 views
6

Autor przedstawił this code pod tytułem A bus error on my platformCo może pójść źle, jeśli cout.rdbuf() jest używane do przełączania bufora i nigdy go nie przywraca?

#include <fstream> 
#include <iostream> 

int main() 
{ 
    std::ofstream log("oops.log"); 
    std::cout.rdbuf(log.rdbuf()); 
    std::cout << "Oops!\n"; 
    return 0; 
} 

Ciąg "Ups! \ N" jest drukowana do pliku "oops.log". Kod nie odtwarza streambuf couta, ale VS2010 nie zgłosił błędu runtime.

+1

Powinieneś rozwinąć więcej na swoim tytule. – 0x499602D2

Odpowiedz

9

Ponieważ log i std::cout udostępniają bufor, bufor ten zostanie prawdopodobnie zwolniony dwukrotnie (jeden raz, gdy log wykracza poza zakres, a następnie jeszcze raz, gdy program się zakończy).

Powoduje to niezdefiniowane zachowanie, więc trudno jest określić dokładny powód, dla którego powoduje błąd magistrali na jego komputerze, ale po cichu zawodzi.

+0

Przyjmuję twoją odpowiedź, ponieważ była to pierwsza, którą otrzymałem. Tks. –

+0

Jakikolwiek sposób obejść to? – Caesar

+1

@Caesar, tak, nie udostępniając buforów strumieniowych w pierwszej kolejności. Zastanawiam się, co pierwotny autor próbował osiągnąć, robiąc to. –

3

Twój program ma niezdefiniowane zachowanie.

Destruktor obiektu globalnego cout usunie bufor strumienia, gdy wykracza poza zakres, a to samo dotyczy log, który również jest właścicielem tego samego bufora strumieniowego. W ten sposób dwa razy usuwasz ten sam obiekt.

Gdy program ma niezdefiniowane zachowanie, wszystko może się zdarzyć, od sformatowania dysku twardego do zakończenia bez żadnych błędów.

Na mojej platformie, na przykład, program wchodzi w nieskończoną pętlę po powrocie z main().

4

Ponieważ inne odpowiedzi nie zawierają wzmianki o tym, co należy zrobić, podam to tutaj. Musisz zapisać i przywrócić bufor, którym ma zarządzać cout. Na przykład:

#include <fstream> 
#include <iostream> 

// RAII method of restoring a buffer 
struct buffer_restorer { 
    std::ios &m_s; 
    std::streambuf *m_buf; 

    buffer_restorer(std::ios &s, std::streambuf *buf) : m_s(s), m_buf(buf) {} 
    ~buffer_restorer() { m_s.rdbuf(m_buf); } 
}; 

int main() 
{ 
    std::ofstream log("oops.log"); 
    buffer_restorer r(std::cout, std::cout.rdbuf(log.rdbuf())); 
    std::cout << "Oops!\n"; 
    return 0; 
} 

Teraz gdy bufor cout „s otrzymuje przed cout ulega zniszczeniu pod koniec programu, więc kiedy cout niszczy buforze poprawna rzecz dzieje.


Dla zwykłego przekierowania standardowego io ogólnie środowisko ma już taką możliwość (np. Przekierowanie io w powłoce). Zamiast powyższego kodu I pewnie po prostu uruchomić program jako:

yourprogram > oops.log 

Również jeden rzeczą do zapamiętania jest to, że std::cout jest zmienna globalna z wszystkich tych samych wad jak inne zmienne globalne. Zamiast modyfikować lub nawet używać go, możesz użyć standardowych technik, aby uniknąć zmiennych globalnych. Na przykład możesz przekazać parametr std::ostream &log_output i użyć go zamiast używać kodu bezpośrednio pod numerem cout.

Powiązane problemy