1 Востаннє редагувалося incred (18.01.2014 15:51:13)

Тема: Стандартні потоки С++.

Шановне панство! Питання життя і смерті. Я й раніше плавав у потоках, а зараз треба якнайдетальніше розібратися із ними. Є "кілька питань", я запишу їх у формі своїх доводів, а вас прошу підтвердити або спростувати їх. Ретельно прогуглити не маю часу(та й, чесно кажучи, бажання також), тож рятуйте.

1. Поток - об'єкт ios-послідовника. Чи можна створити об'єкт типу ios, і наскільки можливості будуть обмеженими?

2. Розумна книжка сказала: об'єкти cerr і clog не можуть бути перенаправленими. Що це означає? Що означає "перенаправити" потік? Можливо, в якесь відмінне від командного рядку місце? І взагалі, що означає, направити його, куди направлені cout та cin?

3. ios має вказівник на streambuf. Це клас, що відкриває/закриває потік на найелементарнішому рівні. Він є, таким собі, посередником між ios та ОЗУ.

4. Що означає "cerr не проходить буферизацію"? Тут я тоді не зрозумів: якщо ios (чи що там інше) може вивести і без буферизації, то навіщо вона тоді потрібна? Може у streambuf'і і виявляються хренові біти?

5. Чому зберігається саме вказівник на streambuf, а не цілий об'єкт? Можливо, через ефективність, якої можна досягти в умовах частого послідовного відкривання/закривання потоків і відповідно динамічного створення/видалення об'єкту. Але це слово "послідовно" мене насторожує. Адже потоків може бути відкрито одночасно кілька(теоретично, це можливо). Як тоді працює streambuf? Не може ж бути у кожного файлу власний streambuf. Тоді б була така плутанина, якщо кожному треба то відкрити, то закрити потік, хоча... якщо це вказівник, і пам'ять виділяється нестатично, тобто не з стеку, а з купи, там такої проблеми не повинно б бути.
Або може Страуструп передбачав часту зміну інтерфейсу/реалізації streambuf'а і хотів уберегти користувачів бібліотеки std від перекомпіляції цілого проекту.

6. Шо се таке:

(cin>>size[i]).get();

7. Я працював над створенням функцій, що безпечно отримують з потоку true-дані у відповідності до певного типу. Це було так: отримав стрінгу, розпарсив її і вирішив, що робити вже далі: бед-біт ставити чи що далі робити. Як це реалізовано в "operator>>" із стандартними типами? Адже там однак отримується відпочатку рядок символів, а у streambuf'і вже він, мабуть, розбирається? Якщо не зрозуміло, то так: що робить cin>>intVar; якщо, як не крути, маємо спочатку рядок символів?

8. Всілякі failbit, badbit можна встановлювати у будь-якому потоці, бо всі класи наслідують ios, у якому і визначаються всі флаги.

9. Як встановлюються failbit, badbit самою cin.get(...)? Як вона може зрозуміти, що вхідні дані несумісні із типом, який ці дані отримує? Чи тут також працює якась таємнича сила оператору ">>"(і це, скоріше за все, є підтвердженням того, про що я казав у п.7: спочатку маємо рядок символів)?

10. Коли встановлюється якийсь флаг статусу помилки, програма, ніби, завмирає. Що насправді відбувається? Я ж можу спитати у потока, чи все добре і те галудження буде працювати.

cin>>var;    //тут користувач шось не те тикає

if( !cin.good() ) {        //і що ми маємо:
    //цей код виконується,
    //і якось починаємо виправляти помилку.
}

Я думаю, що просто завмирає сам потік. Програма починає виконуватися далі, а при намаганні скористатися потоком із бед-бітом вся інформація ігнорується.

11. Я не пробував, але мені здається, що при failbit у якомусь потоці протилежно направлений потік може нормально працювати(н-д, у вхідному якийсь ерор, а вихідний може писати). Чи я правий?

12. Що означає відкрити файл? Пишуть, що "отримати доступ", але на діях - це що? Я думав, що це "завантажити до оперативки".

13. Коли я, прочитавши з файлу щось, перемістився на, припустимо, 20 символів, то тепер вказівник об'єкту вказує на ту позицію, на якій я закінчив зчитування.

14. Я ж можу отримати з файлу кілька рядків одночасно в одну змінну таким чином?

char* str[MAX];            //рядок для вхідних даних
somefile.getline(str, MAX, ifstream() );    //param1 - рядок, у який зчитуємо; 
                            //param2 - довжина; param3 - символ, на якому
                            //закінчуємо зчитування. Пустий конструктор 
                            //ifstream() повертає символ кінця потоку

Дякую за те, що дочитали до цього рядка, уявляю той піздецьжах, який охопив вас, мабуть вже на 7 пункті, але іншого виходу не маю, вибачайте.

Ще одне питання не по темі:
В чому перевага static_cast<T>(U), reinterpret_cast<T*>(U*) перед тими, що С++ дістав від С?
(Про унікальність і небезпечність const_cast<T&>(const T) я знаю.)

2

Re: Стандартні потоки С++.

1. Спробуйте. Тільки зверніть увагу на його конструктори, гадаю, все стане зрозуміло.
2. Будь-яка консольна програма в *nix і має потоки вводу, виводу і помилок. Зазвичай вони працюють з консоллю, але можуть бути перенаправлені на інші файли. Почитайте щось про оболонку (shell) чи хоча б командний рядок Windows.
3. До чого тут ОЗУ? Всі об'єкти розташовані в ОЗУ.
4. Буферизація потрібна для прискорення роботи системи. Наприклад, програма виводить на диск по одному символу, а кожна операція запису на диск довга, тому всі символи спершу збираються докупи, а потім однією операцією записуються.
5. Бо кілька потоків в програмі можуть працювати з одним буфером.
6. А що, по-вашому, повертає std::istream::operator>>?
7. Саме так все і працює. І розбирається не в буфері - буфер тільки тримає байти, а розбирає їх потік.
8. Це питання?
9. Все саме так і відбувається: operator>> розбирає рядок, якщо бачить, що щось не те - ставить badbit.
10. Саме так. І вам легше і корисніше було б написати власний код для перевірки, а не питати в інших.
11. Не існує "протилежно направлених потоків", потоки незалежні (якщо не користуються одним streambuf-ом, звісно).
12. Це означає "повідомити системі, що ми будемо працювати з файлом, тому його не можна давати іншим і краще підвантажити до ОЗУ". Чи буде він підвантажений повністю чи частково - це вже як система вирішить.
13. Див. п. 8
14. Спробуйте, відпишитеся про результат. Він вам буде корисним.
15. В тому, що перетворення - часто ознака того, що щось тут не так. Тому конструкції перетворення такі складні і неоковирні - щоб, по-перше, ними рідше користувалися, а по-друге, одразу бачили, де йде перетворення. Втім, базові типи, н.м.д., можна і без static_cast перетворювати, а от решту - кастами.
П.С. Конструктор нічого не повертає.

Подякували: Logans, incred2