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 http://fara.cs.uni-potsdam.de/~kaufmann/?page=lokiport (файл Functor.h), см. также http://www.geocities.com/rani_sharoni/LokiPort.ht ml (VC7) и конечно оригинал http://moderncppdesign.com/

#include ‹stdio.h›

int main() {

 struct base {

  void operator()(int x) { printf('void base::operator()(%d) ',x); }

 };

 struct derived: base {};

 derived()(5);

 return 0;

}

yaroslav_v 1.5.2003 9:55
Вы читаете Делегаты на C++
Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

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

Отметить Добавить цитату
×