Написание приложений Win32 с помощью одних классов C++ (часть 3) - Переделанные возможности

ОГЛАВЛЕНИЕ

Переделанные возможности

Самая первая тяжелая переделка касается Wrp.h. Ядро было переделано для улучшения производительности и работы.

Модификация карт оберток

В предыдущих версиях все обертки наследуют функцию типажей, приводящую к LPVOID обернутый тип. Это значение используется как ключ в статической глобальной обратной карте (XMap), позволяющей найти XRefCount_base, связанный с обернутым значением (обычно описатель Windows или указатель).

У вышеназванного есть два главных недостатка.

Первый: Рассмотрите приложение с сотней окон и "документ" (данные приложения), состоящий из коллекции тысячи полиморфных объектов, поддерживаемых интеллектуальными указателями (являющимися обертками указателей!). В результате обратная карта становится огромной и медленной и просматривается очень часто (почти всегда, когда Windows отправляет сообщение).

Второй: Рассмотрите объект, обернутый обертками разных типов, использующий разные типы подсчета ссылок (например, для хранения разных видов информации). Если один и тот же экземпляр объекта обернут несколькими обертками разных типов, потому что может существовать лишь один счетчик ссылок, некоторые обертки не найдут нужные данные. Это проблема из-за отсутствия "безопасности типов".

Специализация карт

Специализация карт предотвращает вышеназванное. Чтобы сделать это:

•  Структуры "types_wrp_xxx" struct определяют новый typedef по имени TKey, имеющий менее обобщенное значение, чем LPVOID. Это позволяет иметь разные EMap, специфичные для разных types_wrp. TKey обычно является псевдонимом для типа H, но может отличаться в отдельных случаях.
•  Функция traits_wrp_xxx::Key сейчас возвращает TT::TKey.
•  Функции Attach и Detach в WrpBase были изменены в соответствии с новыми типами: TT::TKey вместо LPVOID.

В частности:

types_wrp_hnd<H>typedef typename H TKey;
types_wrp_ptr<H>typedef typename LPVOID TKey;
types_wrp_rsrc<Data>typedef typename HRSRC TKey;

Из-за этих определений описатели имеют одну карту на каждый тип (одну для каждого H), тогда как указатели имеют одну глобальную карту (из LPVOID). Это обеспечивает работу полиморфизма (указатели разных типов могут указывать на разные компоненты –базовые классы - одного и того же сложного объекта: он должен иметь одну идентичность).

Еще одна проблема, требующая решения, заключается в том, что те карты, объявленные внутри статических функций, создаются, когда требуются впервые. Но что если они требуются глобальной обертке, указателю или событию, создающему их после конструктора глобального «потребителя». И в результате их уничтожение произойдет раньше, вызывая нарушение целостности информации в памяти.

Во избежание этого они должны уничтожаться как можно позже. Это достигается путем создания карт в куче и связывания их в глобальный список, экземпляр которого создается глобально как можно скорее, и который уничтожает содержимое при удалении.

Для этого служит EMap_base вместе с EMap<> и EMap<>::TMap и скрытым XInit. Также в отладочном режиме была добавлена статистическая характеристика для карт, показываемых при завершении программы.

PtrCreator

NWrp::Ptr не дает сбой при непостоянном разыменовании NULL. В этом случае он вызывает "функцию автоматического создания", передаваемую как шаблонный параметр (если NULL, предполагается статическая функция, делающая "new T"). SetAutoCreateFn может быть вызвана при выполнении для изменения функции создания (например, для передачи функции, делающей "new" для типа, производного от T).

Поскольку такой функционал обычно нужен для динамических и полиморфных типов, он существует только в виде NWrp::PtrCreator<class, ctretorfunction>::Dynamic.

Безопасность типов

Для улучшения безопасности типов было пересмотрено отношение между оберткой, счетчиками ссылок и картами. XRefCount сейчас реализован с помощью общего базового класса, от которого наследуют все счетчики ссылок, и сообразно назван SRefCount. Но "счетчик владельцев" (то есть счетчик, определяющий время жизни обернутого объекта) больше не входит в сам SRefCount, но на него ссылается статическая карта с использованием того же TKey обертки, для которой предназначен счетчик SRefCount.

Это позволяет иметь счетчик ссылок разных классов, считающий вместе по одному и тому же значению, связанному с одним и тем же ключевым объектом, даже хранящим разные глобальные карты.

XRefChain (теперь названный SRefChain<W>) унаследован от XRefChain_base<W,D>, который, в свою очередь, унаследован от XRefCount_base<D>.  W является оберткой, для которой предназначен счетчик ссылок или цепь, а D является конечным производным классом самого счетчика ссылок (который W дает в качестве своего TRefCount).

WrpBase<H,W,t_trais,t_refCount> теперь имеет структуру SRefCount struct в качестве  значения по умолчанию для t_refCount. Образующие цепь обертки унаследованы от WrpBase (ранее они были тем же самым) как WrpChainBase<H,W,t_traits,t_refCount> и имеют дополнительные функции для получения итераторов через цепь оберток. Ожидается, что они имеют SRefChain или производный счетчик ссылок и соединитель (t_refCount принимает значение по умолчанию SRefChain<W>).

Поскольку счетчик ссылок и обертка теперь знают о своих соответствующих классах, все функции и обратные вызовы теперь типобезопасны (приведение типов не требуется).

Владеющие интеллектуальные указатели сейчас получаются как Ptr<Type>::Static или Ptr<Type>::Dynaimc (первый преобразует типы с помощью static_cast, второй – с помощью dynamic_cast). Наблюдающие указатели - Qtr<Typr>::Static и::Dynamic. Любую обертку можно превратить из наблюдателя во владельца путем вызова функции-члена SetOwnership. Ptr и Qtr – всего лишь сокращения с разным поведением по умолчанию, но, по сути, равноценны по возможностям.

EAutodeleteFlag

Этот класс изначально является источником для логического флага и функцией для его извлечения. Он используется в EWnd, где "автоматическое удаление" реализовано посредством "delete this", ссылающегося на обертку, наблюдающую WM_NCDESTROY (последнее сообщение, наблюдаемое окном в своей жизни). Это хорошо для оберток окна, существующих в куче и принадлежащих обернутому ими окну.

Но тут есть потенциальная проблема: допустим, программа создает экземпляр немодального окна инструментов без хозяина: это всплывающее окно, не являющееся потомком главного окна. Допустим, главное окно закрывается. Все потомки уничтожаются, и отправляется WM_QUIT (автоматически, если у окна есть обертка EWnd). После ухода всех сообщений завершается цикл обработки сообщений. Но всплывающее окно все еще существует: никто не удалил его.

Для операционной системы это не проблема (она уничтожит всплывающее окно после возврата WinMain), но отправка сообщений отсутствует. Следовательно, никакое WM_NCDESTROY не обрабатывается оберткой всплывающего окна, сохраняющейся после завершения программы (фактически, это утечка!).

Во избежание этого EAutodeleteFlag соединяются в статический список при установке "включено" и удаляются при установке "выключено". Уничтожение списка (при завершении программы) удаляет все существующие объекты.

Хитрость показана ниже:

class EAutodeleteFlag
{
protected:
    bool _bHasAutodelete;
    struct XChain: public std::list<EAutodeleteFlag*>
    {
        ~XChain()
        { ... }
    };
    static XChain& AutoDeleteChain() { static XChain c; return c; }
public:
    EAutodeleteFlag() { _bHasAutodelete = false; }
    virtual ~EAutodeleteFlag() { AutoDeleteChain().remove(this); }
    bool HasAutodelete() const { return _bHasAutodelete; }
    void Autodelete(bool bOn)
    {
        if(bOn && !_bHasAutodelete) AutoDeleteChain().push_back(this);
        if(!bOn && _bHasAutodelete) AutoDeleteChain().remove(this);
        _bHasAutodelete = bOn;
    }
};