Как правильно управлять временем жизни для CCmdTarget, используемого в качестве приемника событий? - PullRequest
2 голосов
/ 27 июля 2011

Я следую примеру Microsoft, приведенному для «Как создать интерфейс приемника в COM-клиенте на основе MFC» , чтобы создать приемник событий в C ++ (VC6).Источником события является сборка .NET, которая демонстрирует свою функциональность через взаимодействие COM.

Подходит мне то, что мне нужно, это последняя заметка образца:

Поскольку CMySink былсозданный в куче, убедитесь, что вы удалили его, чтобы избежать утечек памяти.

Несколько вещей, которые я отмечаю:

  • Параметр автоинкремента / декрементаДля GetIDispatch и AfxConnectAdvise / AfxConnectUnadvise установлено значение FALSE, поэтому я предполагаю, что внутренний счетчик ссылок для приемника остается неизменным на протяжении всего упражнения.
  • Метод OnFinalRelease не показан в упражнении, поэтому я предполагаю, что онэто стандартное поведение удаления экземпляра приемника.

Учитывая, что последнее примечание в тексте примера, мой код очистки выглядит примерно так:

//Get a pointer to sinks IUnknown, no AddRef.
LPUNKNOWN pUnkSink = m_pSink->GetIDispatch(FALSE);

//Terminate a connection between source and sink.
//m_pUnkSrc is IUnknown of server obtained by CoCreateInstance().
//m_dwCookie is a value obtained through AfxConnectionAdvise().
AfxConnectionUnadvise(m_pUnkSrc, IID_MYEVENT, pUnkSink, FALSE,
   m_dwCookie);
delete m_pUnkSink;

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

Я попытался удалить вызов delete m_pUnkSink.В результате OnFinalRelease никогда не вызывается.Это оставляет меня с утечкой памяти, так как все эти экземпляры приемника накапливаются в куче.

Я думаю, что я мог бы избежать повторного использования одного и того же экземпляра приемника для каждой итерации цикла, ноМне любопытно, что такое правильное управление временем жизни.Нужно ли делать явный выбор между удалением экземпляра самостоятельно и переопределением OnFinalRelease, чтобы ничего не делать, вместо того, чтобы никогда не удалять себя и всегда ожидать, что OnFinalRelease выполнит удаление?Один предпочитает другому?

1 Ответ

1 голос
/ 27 июля 2011

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

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

Обновление:

Если реализация по умолчанию нежелательна, как это часто бывает с MFC, тогда ваш лучший вариант - переопределить virtual OnFinalRelease и заставить его ничего не делать вместо вызова delete this;, как это происходит в CCmdTarget. Тогда это полностью на вас, чтобы удалить свой объект и очистить после себя. Я бы сделал ваш объект подсчета ссылок на классы приемников или поместил бы его в какой-нибудь умный указатель вроде std::unique_ptr, если у вас есть поддержка C ++ 0x.

...