Это - копия документа, находившегося на http://dz.ru. Авторские права, если не указано иначе, принадлежат Дмитрию Завалишину и/или Евгении Завалишиной. Все изменения, внесенные мной, находятся в этой рамочке.Пожалуйста, прочитайте disclaimer. |
К слову о DVD Audio. Разгромная статья на www.audio.ru. Содержание вкратце: реальной техники для 20- и 24-битной и 96-килогерцовой звукозаписи нет, то, что есть - даёт качество звука хуже, чем старая 44КГц/16 бит, а вся 20-битность - от того, что кому-то золотой цепки и растопыренных пальцев хватать перестало, чтобы всласть попонтоваться.
С чем в этой статье приходится согласиться, так это с тем, что нынче с аппаратурой 96/24 действительно туговато и ожидать, что завтра на прилавки посыплются качаственные DVD Audio диски в этом формате не приходится.
Но в целом пессимизм автора мне непонятен, признаюсь. Да, хай-энд вообще для понтовиков-затейников - излюбленная площадка в плане порезвиться. Да, сегодня должного железа нет. Ну что ж с того. Будет. Оно ведь и не появится, пока нет реальной потребности.
Более всего автора статьи заботит джиттер, который в 44/16 уже придавили, а в 96/24 - никак. Что это такое - поясню на примере.
Предположим, я должен передать другу 100 бумажек по рублю. Делать это мы условились таким образом. Он закроет глаза и будет раз в 5 секунд выставлять руку вперед и хватать ею то, что попадётся. Я закрою глаза и буду раз в 5 секунд выставлять руку с рублём вперед и надеяться, что его схватят. Предусмотрительно точка встречи рук гарантирована внешними средствами, так что рука мимо руки в пространстве не промахивается. Зато во времени!.. Если наши часы идут немного по-разному, то через несколько минут от начала процесса моменты, в которые я помещаю рубль и он его забирает начнут расходиться. Если я буду подавать рубли медленнее, то в определенный момент он схватит воздух, если быстрее, то он попытается унести один рубль дважды, то есть всё равно получит во второй раз воздух.
Джиттер - это неспособность приёмника и передатчика цифрового потока твёрдо разобраться, где начинается второй бит и кончается первый. Бывает только в синхронных системах, то есть там, где работают с закрытыми глазами.
Лечится открыванием глаз, то есть переходом от синхронных к асинхронным методам передачи информации. Впрочем, неважно, чем. Главное - лечится.
Местные новости.
Московские телефонисты решили перекроить телефонные коды. Во-первых, префиксы 5xx-xxxx из зоны 095 (Москва) будут отбираться у подмосковных АТС и возвращаться Москве. Для Подмосковья выделяется дополнительный к имеющемуся 096-му код 498. В далёком будущем, вероятно, код 096 будет вообще освобожден, а соответствующие АТС переведены на код 498.
C++ is an object oriented language. Right ? There is a class in the standard library map. Look at this method: map::insert pair<iterator, bool> insert(const value_type& x); iterator insert(iterator it, const value_type& x); void insert(const value_type *first, const value_type *last); The first member function determines whether an element y exists in the sequence whose key matches that of x. (The keys match if !key_comp()(x. first, y.first) && !key_comp()(y.first, x.first).) If not, it creates such an element y and initializes it with x. The function then determines the iterator it that designates y. If an insertion occurred, the function returns pair(it, true). Otherwise, it returns pair(it, false... Isn't it cute what you can do with the pair template ???? Isn't that a bit confusing??? Isn't this pair thing a bit arbitrary? You can do whatever you want! - Same thing like having two return values from a function.
|
Спасибо, пример просто божественный. Манна небесная - наверное, ничего лучше для иллюстрации моих взглядов на жизнь и придумать нельзя. :-)
Что касается самого примера - это, насколько я помню, кусок из STL - библиотеки, которую сами авторы объектно-ориентированной не называют. Да, она написана на ++, но между "написано на ++" и "ОО" нет никакой связи. То есть ни первое не проистекает из второго, ни наоборот. Главное - она не использует исключения. Увы, это порождает массу неприятностей. Собственно, не важно, как мы вернули два значения - через структуру или темплейт - второе тоже структура, просто параметризованная. Кошмар еще больший, чем тупо в лоб, и я не понимаю, почему уж если они решили писать не в парадигме ОО, то нельзя было сделать:
map::insert iterator insert(const value_type& x, bool &ok);
Те же яйца, только без этих ужасных беспричинный наворотов. Если же писать ОО-но, то нужно было сделать, к примеру, так:
map::insert iterator insert(const value_type& x, bool &ok) { ... if( can_t_insert ) throw Exception_Invalid_Arg("Duplicate value"); ... }
Чем это принципиально отличается от возврата двух значений. Тем, что исключительная ситуация более адекватно отражает суть события. Когда функция insert, от которой требуется вставить, не смогла выполнить свою задачу, она не имеет права вообще возвращаться - понятие возвращаемого значения просто к этой ситуации не применимо. "Возврат" через exception же избавляет ее от этой маразматической необходимости - искусственно увеличивать число состояний возвращаемого объекта с тем, чтобы часть их выделить под "не шмогла". При использовании исключений функция или сделала и вернула, или не вернулась.
Практически это СИЛЬНО-СИЛЬНО повышает читабельность кода. Функции, возвращающие два значения или возвращающие одно через пойнтер, можно использовать только одним образом - в гордом одиночестве, а если они еще и возвращают флаг успеха то нужен еще и if(), хорошо, если один. Код превращается в вагоны промежуточных переменных и прорвы проверок успешности очередного действия и ковыряний в кучах же ресурсов, когда требуется обслужить неуспех.
Результат: где проверка на успешность работы функции никак не может быть опущена, она как-то делается. В остальных местах на нее забивают, ибо иначе застрелишься. Самые продвинутые умеют писать так, чтобы порождённый в нескольких точках неуспех "стекался" без вреда для работы программы в одну точку и там проверяют, насколько всё правильно проистекало. При этом, очевидно, нельзя сказать, где именно была проблема да и вообще в промежуток времени от появления проблемы до ее поимки программа работает с грязными данными и не всегда последствия этого отслеживаются и изничтожаются.
С применением исключений эти беды отступают - кинуть exception несложно, ловить их, если по уму писать, нужно крайне редко, зато программировать в результате ку-уда проще. Из сопель вида
struct token t; if( parse_token( input, &t ) == Err ) { printf("Syntax error: ....); return Err; } if( insert_in_a_tree( &tree, &t ) == Err ) { printf("Out of memory ....); return Err; }
получается
tree.insert( input.parse() );
и где-то выше, чуть ли не в main, один раз:
try { compile(); } catch( Ex_general &ex ) { printf("Error in %s: %s (%s)\n", ex.where(), ex.what(), ex.who() ); }
Отмечу, что при активном использовании исключений начинают играть такие приятности ++, как автоматическое преобразование типов через конструкторы. (Кстати, невозможность запрета этого фокуса - один из крупных недостатков ++, но сейчас - не об этом.)
Известно, что если ++-компайлер знает, что тут вот нужен тип А, а ему суют Б, то он попробует преобразовать Б в А вызвав конструктор вида А( &Б ). На практике пользоваться этим очень трудно, так как преобразование не любого типа в любой проходит гарантированно гладко. Иногда выясняется, что данный tm в time_t не преобразуется, гад. Если в программе применяются exceptions, то есть исключение можно кинуть где угодно, и оно не явится для окружающего кода неожиданностью, то нет проблем - пишем конструктор, если он не может сконструировать - дохнет через exception.
Цена. Ничто не бесплатно. Применение исключений порождает свои проблемы.
Производительность. Часто полагают, что она падает. Обоснование - ловушки исключений вносят лишний код, на который тратится процессорное время. Это несправедливая оценка. Да, компилятором привносится неявный код для обработки исключений. Но явный код, который использовался программистом для проверки успешности выполнения функций, пропадает. По моим ощущениям если не идти на полумеры и честно перевести программу на exceptions полностью, то второго пропадает куда больше, чем появляется первого. Однако, я не делал замеров, так что это лишь ощущения.
Сам процесс написания программы при активном использовании исключений несколько меняется. Нужна привычка к тому, что вот этот последовательный код без единого if-а может вдруг взять посредине и перестать исполняться. Кто писал на прологе - тому понять легко, а кто нет - тому трудно. Но возможно. :-)
Еще раз благодарю за письмо и жду еще примеров, в которых требуется возврат двух значений. Мне уже самому интересно - бывают ли таковые при условии следования канонам ОО. (У меня есть ощущение, что бывают, и чтобы их победить нужны транзакции.:-)
PS: Уже после написания этого блока я получил письмо с примером случая, когда возвращать нужно именно несколько независимых значений именно из одной функции - когда одна функция совмещает в себе две или более и избежать этого без серьезной (идиотской) потери производительности нельзя. Массив и вычисление интегральных его характеристик.
Тут вот что приходит в голову. Если речь идет о методах класса-контейнера, то существует простое решение.
class holder { bool changed; int _answer1, _answer2...; void compute() { if(!changed) retrun; // compute answer1, answer2, etc } public: holder() { changed = false; } add_value(...) { canged = true; .... } int answer1() const { compute(); return _answer1; } int answer2() const { compute(); return _answer2; } }
То есть вычисляем то, что удобнее вычислять вместе именно вместе, но возвращаем по отдельности. Нет проблем. Что касается оверхеда от введения пары промежуточных методов, то он грошовый, так как compute(), вероятно, работает долго, раз мы весь этот геморрой затеяли.
Некоторая неудовлетворенность, впрочем, от такого решения всё равно остаётся. Не могу пока её формализовать, но что-то гложет.
Реклама, однако.
Юные и зрелые таланты из России! |
---|
|