k001
k001
:...

April 2032
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30

k001 [userpic]
С++ iostreams

Я скоро забуду, как программировать. А может, и не забуду -- талант ведь вроде не пропьёшь? Но, пока не забыл, хочу написать.

1. В Паскале есть write и writeln. В качестве аргументов можно передать произвольное количество разнообразных переменных и констант, разделённых запятыми, и тебе всё это напечатают. Тривиальный пример:

x := 5;
write('x is', x);


Понятно, что реализовано это грубовато, через какое-то подобие препроцессора, обрабатывающего именно этот особый случай write/writeln. То, что обычно называют "грязный хак".

2. В Си есть printf, с ним такой номер не пройдёт -- то есть произвольное количество аргументов напечатать можно, но придётся указывать тип каждой переменной, которую ты хочешь напечатать (и, таким образом, косвенно -- их количество). То есть, примерно так:

x = 5;
printf("x is %d", x);


Тут %d как раз и говорит о том, что надо напечатать целое число из переменной, которая идёт следующим аргументом. Вероятно, специально для этой возможности -- печатать произвольное количество аргументов -- в Си и добавили возможность делать функцию с переменным числом аргументов -- varargs. То есть всё-таки пришлось тоже делать какой-то хак, пусть и не очень грязный.

Вообще, когда я в институте после Паскаля начал изучать Си, меня это поразило. Компилятор же знает, что это целое число -- нафига я должен ему это два раза говорить (первый раз при объявлении переменной, второй раз -- когда печатаю). "За то ль я гиб и мёрз в семнадцатом году"? Ну, поразился, поудивлялся, потом привык.

3. В Си-плюс-плюс есть cout, которому можно скормить всё и в любом количестве, примерно как и в Паскале, только синтаксис несколько странный:

x = 5;
cout << "x is" << x;


Те, кто знают Си, понимают << как оператор побитового сдвига влево. Тем, кто знает Си++, известно, что (почти) любой оператор можно переопределить, причём полиморфно (для разных типов данных делать разные действия). Это переопределение ещё называют "перегрузка операторов".

И вот, как только я (много лет назад) узнал, что в Си++ для того, чтобы получить что-то, подобное паскалевскому write, перегрузили оператор побитового сдвига влево -- я содрогнулся. Те, кто понимает, как всё это (классы, виртуальные функции, перегруженные операторы и полиморфизм) работает в Си++, и какая титаническая работа "в недрах" проделывается, чтобы напечатать "x is 5", наверное, тоже содрогаются.

И я всё ещё содрогаюсь! При этом в целом мне С++ как язык нравится. Но вот эти iostreams -- это просто какое-то извращение из извращений, какая-нибудь садомазогеронтопедогомофилия по сравнению с этим -- невинная забава.

PS а всё это я к тому, что часто языки критикуют за нестройность, наличие каких-то особых случаев, исключений -- но порой такие исключения нужны. Вот в варианте нумер 3 обошлись, казалось бы, без исключений -- но такую выгородили конструкцию, что уж, право слово, лучше бы сделали исключение в языке вместо этого.

Comments

Исправь паскалевский пример, там ограничители строки — апострофы, а не кавычки.

А вообще тема раскрыта не полностью, ещё есть (format nil "x = ~A" x) и print "x = $x\n" :)

Исправь паскалевский пример, там ограничители строки — апострофы, а не кавычки.

Значит, всё-таки, можно немного пропить талант. :))))
Спасибо, поправил.

Насчёт раскрытия темы — я и так всеми силами старался сделать пост покороче (а он, собака, расползался в разные стороны), ибо это всё-таки пост, а не статья.

Я стесняюсь тебе намекнуть - но ты же знаешь что все эти cout << оптимизируются в очень простой код на стадии компиляции? Особенно если смотреть на gcc4.1

Ты будешь смеяться, но *printf'ы оптимизируются ещё лучше…

Это потому что авторам того же gcc не до оптимизации iostreams - им бы баги со всем остальным выловить :)

Ты таки будешь смеяться, но тесты на производительность cout vs. *printf я проводил при тебе, cout рулит X0:-)

Ты бы ещё std::vector вспомнил…

Едрить… Там vector<bool> был…

Ну-ка объясни, какие такие классы, виртуальные функции и полиморфизм используются для iostreams.

Перегрузка оператора << для iostreams (помимо совсем клинических случаев, которых я в живой природе не видел) разрешается в прямой function call прямо на стадии компиляции.

+1

iostream - врожденная ошибка дизайна.