Почему после каждого успешного вызова QueryInterface () следует вызов Release ()? - PullRequest
11 голосов
/ 19 февраля 2011

Почему после вызова QueryInterface() всегда следует вызов Release()? Например, я видел пример кода из MSDN, как показано ниже:

HRESULT hr = S_OK;
CDecoder *pObj = new CDecoder(&hr);

if (SUCCEEDED(hr))
{
    *ppv = NULL;
    hr = pObj->QueryInterface(riid, ppv);
}
pObj->Release();
return hr;

Может кто-нибудь объяснить, что стоит за Release() звонить сюда?

Ответы [ 3 ]

15 голосов
/ 19 февраля 2011

Это не всегда следует непосредственно так, хотя это довольно часто.

COM-объекты подсчитываются. Когда вы изначально создаете объект, вы получаете указатель на IUnknown. Затем вы получаете другой интерфейс с QueryInterface. Поскольку вам (как правило) больше не нужен интерфейс IUnknown, вы отпускаете его. Когда вы отпустите другой полученный вами интерфейс, счетчик ссылок будет равен 0, поэтому объект может быть уничтожен. Однако если вы не отпустите IUnknown, счетчик ссылок останется ненулевым, поэтому объект не может быть уничтожен.

Наиболее очевидный случай, когда вы не немедленно отпустите IUnknown, это когда / если вам нужно получить более одного интерфейса. В таком случае вы получите IUnknown, затем второй и третий интерфейсы перед выпуском IUnknown. По крайней мере, в некоторых случаях вы можете не знать о третьем (или последующих) интерфейсах сразу после создания объекта, поэтому вам может потребоваться сохранить доступ к IUnknown в течение некоторого произвольного промежутка времени перед его освобождением.

8 голосов
/ 19 февраля 2011

Почему за вызовом QueryInterface всегда следует вызов Release?

Поскольку QueryInterface будет вызывать AddRef , что увеличивает количество ссылокк указателю.Когда имеется 0 ссылок на указатель, он освобождается для вас.

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

Например, если у вас есть объект, который реализует 2 интерфейса, то вызов просто приведёт этот объект к каждому интерфейсу и увеличит переменнуюкоторый используется в качестве счетчика ссылок.

Примечание: счетчик ссылок может быть реализован различными способами, но вышеизложенное объясняет обычный сценарий.В частности, @Ben описывает отрывной интерфейс, ниже которого подчеркивается важность вызова Release для указателя интерфейса, который был вам возвращен.

4 голосов
/ 19 февраля 2011

Этот фрагмент кода заинтересован только в получении значения ppv .Обратите внимание, что указатель интерфейса освобождается , а не .Класс CDecoder, по-видимому, является средством для его получения.Для его создания есть оператор new , который не является стандартным способом COM для создания класса COM, который принимает CoCreateInstance ().Очевидно, для правильного использования этого класса требуется вызов Release () вместо использования оператора delete .Опять же, не совсем стандартно, но не невозможно.Я могу только догадываться, что CDecoder - это класс C ++, который реализует кокласс COM, и этот код использует его напрямую, а не через обычные процедуры COM.Это совсем не так.

...