В новом коде C ++ я предпочитаю использовать библиотеку C ++ iostream вместо библиотеки C stdio.
Я заметил, что некоторые программисты придерживаются stdio, настаивая на его переносимости.
Так ли это на самом деле? Что лучше использовать?
В новом коде C ++ я предпочитаю использовать библиотеку C ++ iostream вместо библиотеки C stdio.
Я заметил, что некоторые программисты придерживаются stdio, настаивая на его переносимости.
Так ли это на самом деле? Что лучше использовать?
Чтобы ответить на исходный вопрос:
Все, что можно сделать с помощью stdio, можно сделать с помощью библиотеки iostream.
Disadvantages of iostreams: verbose
Advantages of iostreams: easy to extend for new non POD types.
Шагом вперед, который C ++ сделал над C, стала безопасность типов.
iostreams был разработан с учетом явной типобезопасности. Таким образом, при назначении объекту явно проверяется тип (во время компиляции) присваиваемого объекта (при необходимости генерируется ошибка времени компиляции). Таким образом предотвращается переполнение памяти во время выполнения или запись значения с плавающей запятой в объект типа char и т. Д.
scanf () / printf () и семейство, с другой стороны, полагаются на то, что программист получит правильную строку формата, и не было никакой проверки типа (я считаю, что у gcc есть расширение, которое помогает). В результате он стал источником многих ошибок (поскольку программисты менее совершенны в своем анализе, чем компиляторы (не говоря уже о том, что компиляторы идеальны, просто лучше, чем люди)).
Просто чтобы прояснить комментарии Колина Дженсена.
Для пояснения комментариев Микаэля Янссона.
N.B. Я согласен с тем, что библиотека iostream немного многословна. Но я готов смириться с многословием, чтобы обеспечить безопасность во время выполнения. Но мы можем уменьшить многословие, используя Библиотеку форматов Boost < / а>.
#include <iostream>
#include <iomanip>
#include <boost/format.hpp>
struct X
{ // this structure reverse engineered from
// example provided by 'Mikael Jansson' in order to make this a running example
char* name;
double mean;
int sample_count;
};
int main()
{
X stats[] = {{"Plop",5.6,2}};
// nonsense output, just to exemplify
// stdio version
fprintf(stderr, "at %p/%s: mean value %.3f of %4d samples\n",
stats, stats->name, stats->mean, stats->sample_count);
// iostream
std::cerr << "at " << (void*)stats << "/" << stats->name
<< ": mean value " << std::fixed << std::setprecision(3) << stats->mean
<< " of " << std::setw(4) << std::setfill(' ') << stats->sample_count
<< " samples\n";
// iostream with boost::format
std::cerr << boost::format("at %p/%s: mean value %.3f of %4d samples\n")
% stats % stats->name % stats->mean % stats->sample_count;
}
fprintf
работает с любым FILE
объектом, который может быть создан из любого файлового дескриптора. Таким образом, он также одинаково хорошо работает с различными механизмами ввода / вывода: со всем, что может быть реализовано как файловый дескриптор.
- person Tom; 23.12.2010
Это слишком многословно.
Подумайте о конструкции iostream для выполнения следующих действий (аналогично scanf):
// nonsense output, just to examplify
fprintf(stderr, "at %p/%s: mean value %.3f of %4d samples\n",
stats, stats->name, stats->mean, stats->sample_count);
Для этого потребуется что-то вроде:
std::cerr << "at " << static_cast<void*>(stats) << "/" << stats->name
<< ": mean value " << std::precision(3) << stats->mean
<< " of " << std::width(4) << std::fill(' ') << stats->sample_count
<< " samples " << std::endl;
Форматирование строк - это случай, когда объектно-ориентированность можно и нужно обойти в пользу форматирования DSL, встроенного в строки. Рассмотрим Lisp format
, форматирование в стиле printf в Python или PHP, Bash, Perl, Ruby и их строковую интраполяцию.
iostream
для этого варианта использования в лучшем случае ошибочен.
operator<<
для своей статистики, тогда клиенты могут использовать ее как std::cout << stats;
, что явно превосходит printf(...)
или пользовательские функции печати, которые должны будут принимать ФАЙЛ-дескриптор.
- person Sebastian Mach; 24.11.2011
Boost Format Library предоставляет типобезопасную объектно-ориентированную альтернативу форматированию строк в стиле printf и является дополнением к iostreams, которое не страдает от обычных проблем с многословием из-за умного использования оператора%. Я рекомендую подумать об использовании простого C printf, если вам не нравится форматирование с помощью оператора iostream ‹----------------.
В старые добрые времена комитет по стандартам C ++ возился с языком, и iostreams были движущейся целью. Если вы использовали iostreams, у вас была возможность переписывать части вашего кода каждый год или около того. Из-за этого я всегда использовал stdio, который существенно не изменился с 1989 года.
Если бы я делал что-то сегодня, я бы использовал iostreams.
Если, как и я, вы выучили C до того, как изучать C ++, библиотеки stdio кажутся более естественными в использовании. У iostream и stdio есть свои плюсы и минусы, но я пропускаю printf () при использовании iostream.
В принципе, я бы использовал iostreams, на практике я использую слишком много форматированных десятичных знаков и т. Д., Что делает iostreams слишком нечитаемыми, поэтому я использую stdio. Boost :: format - это улучшение, но для меня недостаточно мотивации. На практике stdio почти безопасен по типу, поскольку большинство современных компиляторов все равно проверяют аргументы.
Это область, в которой я все еще не совсем доволен ни одним из решений.
Для двоичного ввода-вывода я обычно использую fread и fwrite из stdio. Для форматированного материала я обычно использую IO Stream, хотя, как сказал Микаэль, нетривиальное (нестандартное?) Форматирование может быть PITA.
Я буду сравнивать две основные библиотеки из стандартной библиотеки C ++.
Существует несколько причин, чтобы смягчить их использование:
Плох не только printf сам по себе. Программное обеспечение стареет, подвергается рефакторингу и модификации, а ошибки могут быть внесены из удаленных мест. Предположим, у вас есть
.
// foo.h
...
float foo;
...
и где-то ...
// bar/frob/42/icetea.cpp
...
scanf ("%f", &foo);
...
И через три года вы обнаружите, что foo должен иметь какой-то нестандартный тип ...
// foo.h
...
FixedPoint foo;
...
но где-то ...
// bar/frob/42/icetea.cpp
...
scanf ("%f", &foo);
...
... тогда ваш старый printf / scanf все равно будет компилироваться, за исключением того, что теперь вы получаете случайные ошибки сегментации, и вы не помните, почему.
Если вы думаете, что printf () менее подробный, то есть определенная вероятность, что вы не используете полную силу их iostream. Пример:
printf ("My Matrix: %f %f %f %f\n"
" %f %f %f %f\n"
" %f %f %f %f\n"
" %f %f %f %f\n",
mat(0,0), mat(0,1), mat(0,2), mat(0,3),
mat(1,0), mat(1,1), mat(1,2), mat(1,3),
mat(2,0), mat(2,1), mat(2,2), mat(2,3),
mat(3,0), mat(3,1), mat(3,2), mat(3,3));
Сравните это с правильным использованием iostreams:
cout << mat << '\n';
Вы должны определить правильную перегрузку для оператора < конечно, вы также можете сделать что-то повторно используемое для printf-like, но тогда у вас снова будет printf (что, если вы замените элементы матрицы на новый FixedPoint
?), помимо других нетривиальных вещей, например вы должны передать дескрипторы FILE *.
Обратите внимание, что строки формата часто считаются спасением при интернационализации, но в этом отношении они ничуть не лучше iostream:
printf ("Guten Morgen, Sie sind %f Meter groß und haben %d Kinder",
someFloat, someInt);
printf ("Good morning, you have %d children and your height is %f meters",
someFloat, someInt); // Note: Position changed.
// ^^ not the best example, but different languages have generally different
// order of "variables"
То есть, строки формата C старого стиля не имеют позиционной информации так же, как и iostreams.
Вы можете рассмотреть возможность использования boost :: format. , который предлагает поддержку явного указания позиции в строке формата. Из их раздела примеров:
cout << format("%1% %2% %3% %2% %1% \n") % "11" % "22" % "333"; // 'simple' style.
Некоторые реализации printf предоставляют позиционные аргументы, но они нестандартны.
Помимо производительности (как указал Ян Худек), я не вижу причин. Но имейте в виду:
«Мы должны забыть о небольшой эффективности, скажем, примерно в 97% случаев: преждевременная оптимизация - это корень всех зол. Тем не менее, мы не должны упускать наши возможности в этих критических 3%. Хорошего программиста такие рассуждения не убаюкивают, он поступит мудро, если внимательно взглянет на критический код; но только после того, как этот код будет идентифицирован »- Кнут
и
«Узкие места возникают в неожиданных местах, поэтому не пытайтесь усомниться в догадках и использовать спидхак, пока не докажете, что именно в этом узкое место». - щука
Да, реализации printf обычно быстрее, чем iostreams, обычно быстрее, чем boost :: format (из небольшого и конкретного теста, который я написал, но он должен во многом зависеть от ситуации, в частности: если printf = 100%, то iostream = 160% , и boost :: format = 220%)
Но не забывайте об этом вслепую: сколько времени вы на самом деле тратите на обработку текста? Как долго ваша программа работает до выхода? Уместно ли вообще возвращаться к строкам формата C, ослаблять безопасность типов, уменьшать возможность рефакторинга, увеличивать вероятность очень тонких ошибок, которые могут скрываться годами и могут проявляться только прямо перед лицом ваших избранных клиентов?
Лично я бы не отступил, если не смогу получить ускорение более чем на 20%. Но поскольку мои приложения практически все свое время тратят на другие задачи, кроме обработки строк, мне никогда не приходилось этого делать. Некоторые написанные мною синтаксические анализаторы тратят практически все свое время на обработку строк, но их общее время выполнения настолько мало, что не стоит усилий по тестированию и проверке.
Напоследок хочу задать несколько загадок:
Найдите все ошибки, потому что компилятор этого не сделает (он может предложить только в том случае, если он вежлив):
shared_ptr<float> f(new float);
fscanf (stdout, "%u %s %f", f)
Если ничего другого, что не так с этим?
const char *output = "in total, the thing is 50%"
"feature complete";
printf (output);
Хотя у C ++ iostreams API много преимуществ, одна существенная проблема связана с i18n. Проблема в том, что порядок замены параметров может варьироваться в зависимости от культуры. Классический пример выглядит примерно так:
// i18n UNSAFE
std::cout << "Dear " << name.given << ' ' << name.family << std::endl;
Хотя это работает для английского языка, в китайском языке фамилия стоит на первом месте.
Когда дело доходит до перевода вашего кода для зарубежных рынков, перевод фрагментов чреват опасностями, поэтому новые l10ns могут потребовать изменений в коде, а не только других строк.
boost :: format, кажется, сочетает в себе лучшее от stdio (одна строка формата, которая может использовать параметры в другом порядке, чем они появляются) и iostreams (безопасность типов, расширяемость).
name.formal_name()
, который вернет правильный результат на основе name.culture
(например).
- person paxdiablo; 23.12.2010
cout << student_name << " has a GPA of " << gpa;
- person R Samuel Klatchko; 03.01.2011
Я использую iostreams, в основном потому, что это упрощает работу с потоком позже (если мне это нужно). Например, вы можете узнать, что хотите отобразить вывод в каком-либо окне трассировки - это относительно легко сделать с помощью cout и cerr. Вы, конечно, можете возиться с конвейерами и прочим в unix, но это не так портативно.
Мне нравится форматирование в стиле printf, поэтому я обычно сначала форматирую строку, а затем отправляю ее в буфер. В Qt я часто использую QString :: sprintf (хотя они рекомендуют использовать QString :: arg). Я просмотрел boost.format тоже, но никак не мог привыкнуть к синтаксису (слишком много%). Хотя мне действительно стоит взглянуть на это.
В iolibraries мне не хватает форматированного ввода.
У iostreams нет удобного способа репликации scanf (), и даже у boost нет необходимого расширения для ввода.
stdio лучше подходит для чтения двоичных файлов (например, преобразование блоков в вектор ‹unsigned char› и использование .resize () и т. д.). См. Функцию read_rest в file.hh в http://nuwen.net/libnuwen.html для пример.
Потоки C ++ могут подавиться большим количеством байтов при чтении двоичных файлов, вызывая ложный eof.
Поскольку iostreams стали стандартом, вы должны использовать их, зная, что ваш код наверняка будет работать с более новыми версиями компилятора. Думаю, в настоящее время большинство компиляторов очень хорошо знают iostreams, и проблем с их использованием возникнуть не должно.
Но если вы хотите использовать функции * printf, то, на мой взгляд, проблем быть не может.