Pull to refresh

Comments 28

Внимание, вопрос. Почему эти вещи понадобилось запихивать в параметры шаблона вместо параметров конструктора? Производительность от этого никак не поменялась, а вот головной боли с отсутствием формального описания интерфейсов этих самых параметров тому, кто будет сей код разбирать, прибавилось. Далее. Теперь, чтобы использовать такой класс, надо знать, какие у него там были параметры, просто взять и передать его куда-нибудь уже не получится.
Основное преимущество как раз передачи типов, а не инстанционированных экземпляров в скорости — не надо вызывать конструктор копирования, нету оверхеда от виртуальных функций, хороший компилятор функторы может заинлайнить к тому-же.

Да, housekeeping'а гораздо больше становиться, по сравнению с передачей в конструктор, но иногда очень надо именно скорость.
оверхеда от виртуальных функций
Это какие у вас требования к производительности, если одна операция чтения 4 байт из памяти считается оверхедом?
А ещё мне не вполне очевидна зависимость необходимости вызова копирующего конструктора от того, передаётся ли тип или экземпляр класса.
Почему эти вещи понадобилось запихивать в параметры шаблона вместо параметров конструктора?

Потому что производительность и оптимизация у компилятора. Потому что тогда нужно брать или наследованные от какого-то предка классы в качестве параметров или указывать в .h-файле ссылки на все остальные.
Это какие у вас требования к производительности

Все бывает в этой жизни :-).
Если шибко активный обмен идет (раз в 50 мсек), то это становится существенно, ИМХО.
У *меня* таких требований к производительности нет, в hi-load или разработке игр такие требования часто есть к определенным участкам кода. Тут даже на x86 архитектуре cache miss — 60 инструкций, что иногда многовато.

Если мы не передаем политику как объект, а создаем сами, то можем проконтролировать что никаких лишних копирований или аллокаций не было.
Как-то не совсем на Александреску похоже… Вы передаете класс политики и создаете его экземпляр как поле вашего класса. При этом во многих частях кода используется нечто вроде
if Policy::to_use() { blah-blah blah }

Александреску же предлагает, насколько я помню, целиком вынести эту функциональность в класс стратегии и наследоваться от него. При этом отделится часть, отвечающая, например, за логгирование в отдельный клас. Для случая, когда функциональность не нужна, делается пустая функция, которая точно так же как и у вас исчезает в момент компиляции. После этого, вы сможете изменить поведение написав новый класс и абсолютно не меняя логику уже написанного кода.

ПС. могу ошибаться, если так — поправьте, буду благодарен :)
Да, теперь уже сам не припомню, чего тогда сделал проверку на уровне основного класса, а не внутри… Вот, сейчас описывал в статье и сам подумал о чем-то подобном.

Ага, вспомнил. Допустим, политики записи в отчет содержит 10 функций. А в пустой стратегии они не нужны — нужна только функция to_use. Вот такой был, кажется, аргумент.
А в чем проблема? Все равно же будет так:
if policy::to_use()
____use_func1()
....
if policy::to_use()
____use_func2()
....
if policy::to_use()
____use_func3()

Да, будет чуть больше функций в EmptyPolicy, но они же все равно пустые. Да и читаться будет проще.
____use_func4()


В любом случае проще написать
use_func1()

use_func2()

use_func3()
use_func4()
Возможно, не спорю. Как буду переделывать библиотеку — так и сделаю, наверное.
Увидел:
>>Искал я какую-нибудь программу для отслеживания обмена по USB, да как-то не мог найти.
Я пользовался тулзой:

В свое время сам очень долго искал подобную тулзу
Спасибо! Это именно то, что я искал!
Шаблоны во многом пересекаются по возможностям с традиционным ООП.

Их основное достоинство — оптимизация в момент компиляции, и тут они зачастую незаменимы в критических к производительности участках. Или когда нужно заменить макросы — тоже зачастую полезно (для принятия решений во время компиляции, но без IFDEFов).

Их основной недостаток — вся реализация вылазит наружу и тянется простынями header-файлов. Это плохо для сложных проектов. Поэтому, если к производительности нет особых требований, то лучше прятать реализацию и использовать обычные классы.

Я сам фанат Александреску, но использовать эти (любые) приемы нужно осознанно и при необходимости.

Пример из данной статьи вполне применим. Я так понимаю, особых требований к производительности не было, но в то же время он скорее учебный и для попробовать — так что почему-бы и нет — результат вполне красивый.
Достоинством шаблонов C++ еще стоит назвать то, что они расширяют возможности языка.
К примеру тот-же boost::any без шаблонов трудно себе представить, не так-ли?
При этом это уже не оптимизация, а архитектурное решение.
UFO just landed and posted this here
UFO just landed and posted this here
А я вот не понимаю одного, зачем использовать try...catch?
Наиболее простая форма организации обработки ошибок
мне тоже принцип ортогональности очень понравился из его книги. Если вовремя понять какие вещи в программе можно разделить на слабо связанные блоки — то дальше в разы легче работать с кодом.
Исключения используются для перехода в конец функции? Могу вас уверить, что это не тот случай, для которого были придуманны исключения. Исключения должны использоваться для обработки исключительных ситуаций. Например, когда произошла ошибка, которая обычно происходит очень редко. У вас же исключение выбрасывается всегда. Не говоря о том, что потом они сразу-же ловятся в той же функции.

Так как исключения могут быть реализованны не очень эффективно, то в результате получается очень медленный переход в конец функции. Рекоммендую для интереса просто прогнать этот код в цикле и посмотреть сколько раз он сможет выбросить и поймать исключение в секунду. Может получиться в 1000 раз медленнее чем эквивалентное решение без исключений.
Особенно пикантно это смотрится на фоне экономии за счет устранения виртуальных вызовов.
И я о том же. Взял простой код:

volatile bool b = true;
try {
if (!b)
throw false;
throw true;
} catch (bool res) {
}

Сделал:
g++ -S -O3 test.cpp

Смотрю, а test.s наполнен вызовами __cxa_allocate-exception или __cxa_throw и прочих.

Аналогичная ситуация и даже для такого кода:

const bool b = true;
try {
if (!b)
throw false;
throw true;
} catch (bool res) {
}
Стратегии описанны как-то странно тоже. Мне очень интересно, как будет выглядеть класс NoStrategy. В нем что, есть все функции относящиеся к Protection, Log и Timer? Если нет, то почему по-умолчанию NoStrategy может использоваться как Prot, как Log и как Timer?
Я в классе NoStrategy вписал статическими все функции, которые используются в шаблонном классе — их там реально не много получилось. Все они ничего не делают (или возвращают код ОК).
Для углубления знаний книга Александреку «Современное проектирование на С++» — то что надо.

Но прежде настоятельно рекомендую прочесть другую его книгу — «Стандарты программирования на C++. 101 правило и рекомендация»

Из «Современного проектирования на C++» я использовал:

Характеристики типов для того, чтобы примирить интерфейсы ATL::ComPtr, сырого указателья и boost::shared_ptr в моем библиотечном классе. В итоге интерфейсная функция моего класса принимает указатель на интерфейс в случае ComPtr, либо const boost::shared_ptr& в случае shared_ptr.
Также в случае сырого указателя производится его обнуление в некоторых местах.

Еще реализовал реестр объектов (что-то наподобие IoC контейнера на C#), который обходится без каких-либо id, окромя интерфейсов (внутри контейнера std::map на основании std::type_info)
Получилось что-то вроде списка объектов разных типов (при том удалось обойтись без dynamic_cast и reinterpret_cast).

На пальцах трудно объяснить, и выходит весьма спорно и запутанно…

В общем для меня открылись некоторые полезные приемы, которые до этого были мне недоступны.
Кроме того, для меня стало возможным (хотя и не простым) понять реализацию того-же boost или stl.
Кроме того, для меня стало возможным (хотя и не простым) понять реализацию того-же boost или stl.


Во-во, самое оно!
Sign up to leave a comment.

Articles