Чтение онлайн

ЖАНРЫ

Делегаты на C++

Шаргин Александр

Шрифт:

};

template‹class TRet TEMPLATE_PARAMS›

class I_DELEGATE: public IComparableDelegate {

public:

 virtual TRet Invoke(PARAMS) = 0;

};

Обратите внимание, что в интерфейсе IComparableDelegate шаблоны не используются. Теперь в терминах этого интерфейса можно реализовать базовый класс CDelegateImpl, который будет отвечать за поддержку списка делегатов. Соответственно, в нём

будут реализованы методы Add, Remove и Invoke.

class CDelegateImpl {

public:

 typedef std::list‹IComparableDelegate*› DelegateList;

 CDelegateImpl(IComparableDelegate* pDelegate = NULL) { Add(pDelegate); }

 ~CDelegateImpl { RemoveAll; }

 bool IsNull { return (m_DelegateList.empty); }

protected:

 void Add(IComparableDelegate* pDelegate) {

if (pDelegate != NULL) m_DelegateList.push_back(pDelegate);

 }

 void Remove(IComparableDelegate* pDelegate) {

DelegateList::iterator it;

for (it = m_DelegateList.begin; it != m_DelegateList.end; ++it) {

if ((*it)-›Compare(pDelegate)) {

delete (*it);

m_DelegateList.erase(it);

break;

}

}

 }

 void RemoveAll {

DelegateList::iterator it;

for (it = m_DelegateList.begin; it != m_DelegateList.end; ++it) delete (*it);

m_DelegateList.clear;

 }

protected:

 DelegateList m_DelegateList;

};

Теперь реализация класса CDelegateX существенно упрощается. В нём останутся только операторы (для которых используется inline-подстановка) и метод Invoke. Только этот метод и будет сгенерирован отдельно для каждой специализации - хороший результат по сравнению с тем, что было раньше. Новая реализация класса CDelegateX будет выглядеть так:

template‹class TRet TEMPLATE_PARAMS›

class C_DELEGATE: public CDelegateImpl {

public:

 typedef I_DELEGATE‹TRet TEMPLATE_ARGS› IDelegate;

 C_DELEGATE(IDelegate* pDelegate = NULL): CDelegateImpl(pDelegate) {}

 C_DELEGATE‹TRet TEMPLATE_ARGS›& operator=(IDelegate* pDelegate) {

RemoveAll;

Add(pDelegate);

return *this;

 }

 C_DELEGATE‹TRet TEMPLATE_ARGS›& operator+=(IDelegate* pDelegate) {

Add(pDelegate);

return *this;

 }

 C_DELEGATE‹TRet TEMPLATE_ARGS›& operator-=(IDelegate* pDelegate) {

Remove(pDelegate);

return *this;

 }

 TRet operator(PARAMS) {

return Invoke(ARGS);

 }

private:

 TRet Invoke(PARAMS) {

DelegateList::const_iterator it;

for (it = m_DelegateList.begin; it!= --m_DelegateList.end; ++it) static_cast‹IDelegate*› (*it)-›Invoke(ARGS);

return static_cast‹IDelegate*› (m_DelegateList.back)-›Invoke(ARGS);

 }

};

Обратите

внимание на появившиеся приведения типов. В данном случае они никак не сказываются на типобезопасности делегатов, так как в списке m_DelegateList могут храниться только указатели на объекты классов CStaticDelegateX и CMethodDelegateX, а эти указатели заведомо приводятся к указателю на IDelegate.

В заключение несколько слов о проблеме производительности. Как уже говорилось, она может возникать из-за распределения объектов делегатов в куче. К сожалению, реализовать делегаты как стековые объекты не представляется возможным, так как для них существенным свойством является полиморфное поведение. Но и тут ситуацию можно существенно улучшить. Поскольку все делегаты централизованно создаются внутри функции NewDelegate, для них вполне возможно написать специализированный аллокатор, который будет распределять память для делегатов быстро и эффективно. Написание такого аллокатора оставляется читателю в качестве упражнения.

Заключение

Хочется отметить, что рассмотренный нами пример реализации делегатов может служить иллюстрацией как сильных, так и слабых сторон языка C++. Слабая сторона C++ - это его сложность. Особенно хорошо она заметна при реализации библиотек на базе шаблонов. Их код трудно читать и ещё труднее писать, так как в них семантическая сложность усугубляется сложностью синтаксической. Сильной же стороной C++ является совершенно невероятная гибкость этого языка. В рамках C++ можно реализовать и бесшовно интегрировать в язык самые разные возможности. Причём сделать это удаётся даже несмотря на грубейшие ошибки и недоработки разработчиков некоторых компиляторов.

Комментарии:

наследование операторов

›Дело в том, что в языке C++ операторы не наследуются.

Это не верно по крайней мере для MSVC++. Более того этот метод используется при написании функтора из библиотеки Loki(файл Functor.h), см. также(VC7) и конечно оригинал

#include ‹stdio.h›

int main {

 struct base {

void operator(int x) { printf("void base::operator(%d)\n",x); }

 };

 struct derived: base {};

 derived(5);

 return 0;

}

yaroslav_v 1.5.2003 9:55
Поделиться с друзьями: