Какво може да се обърка, ако cout.rdbuf() се използва за превключване на буфера и никога не го връща?

Авторът представи този код под заглавието A bus error on my platform

#include <fstream>
#include <iostream>

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

Низът „Oops!\n“ се отпечатва във файла „oops.log“. Кодът не възстановява streambuf на cout, но VS2010 не отчете грешка по време на изпълнение.


person John Kalane    schedule 13.02.2013    source източник
comment
Трябва да разработите повече за заглавието си.   -  person 0x499602D2    schedule 14.02.2013


Отговори (3)


Тъй като log и std::cout споделят буфер, този буфер вероятно ще бъде освободен два пъти (веднъж, когато log излезе от обхвата, след това още веднъж, когато програмата приключи).

Това води до недефинирано поведение, така че е трудно да се каже точната причина, поради която задейства грешка в автобуса на неговата машина, но тихо се проваля на вашата.

person Frédéric Hamidi    schedule 13.02.2013
comment
Има ли начин да се заобиколи това? - person Caesar; 13.02.2013
comment
@Caesar, да, не споделям буфери на потоци на първо място. Чудя се какво се е опитал да постигне оригиналният автор с това. - person Frédéric Hamidi; 13.02.2013

Тъй като другите отговори не споменават какво да направя по въпроса, ще го предоставя тук. Трябва да запазите и възстановите буфера, който cout би трябвало да управлява. Например:

#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;
}

Сега, когато буферът на cout се замени, преди cout да бъде унищожен в края на програмата, така че когато cout унищожи своя буфер, се случва правилното нещо.


За просто пренасочване на стандартен io обикновено средата вече има способността да направи това вместо вас (напр. io пренасочване в обвивката). Вместо горния код вероятно просто бих стартирал програмата като:

yourprogram > oops.log

Също така едно нещо, което трябва да запомните е, че std::cout е глобална променлива със същите недостатъци като другите глобални променливи. Вместо да го модифицирате или дори да го използвате, може да предпочетете да използвате обичайните техники, за да избегнете всички глобални променливи. Например можете да предадете параметър std::ostream &log_output и да го използвате, вместо да използвате кода директно cout.

person bames53    schedule 13.02.2013

Вашата програма има недефинирано поведение.

Деструкторът на глобалния cout обект ще изтрие буфера на потока, когато излезе извън обхвата, и същото важи и за log, който също притежава същия този буфер на потока. Така изтривате един и същ обект два пъти.

Когато дадена програма има недефинирано поведение, всичко може да се случи, от форматиране на вашия твърд диск до прекратяване без грешка.

На моята платформа, например, програмата влиза в безкраен цикъл след връщане от main().

person Andy Prowl    schedule 13.02.2013