Почему после каждого успешного вызова QueryInterface() следует вызов Release()?
Почему 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 ответа
Это не всегда следует непосредственно так, хотя это довольно распространено.
COM-объекты подсчитываются. Когда вы изначально создаете объект, вы получаете указатель на IUnknown
, Тогда вы получите другой интерфейс с QueryInterface
, Так как вы (обычно) не заботитесь о IUnknown
интерфейс больше, вы отпускаете это. Когда вы отпустите другой полученный вами интерфейс, счетчик ссылок будет равен 0, поэтому объект может быть уничтожен. Если вы не отпустите IUnknown
однако счетчик ссылок не будет равен нулю, поэтому объект не может быть уничтожен.
Наиболее очевидный случай, когда вы не сразу отпустите IUnknown
это когда / если вам нужно получить более одного интерфейса. В таком случае вы получите IUnknown
затем второй и третий интерфейсы перед выпуском IUnknown
, По крайней мере, в некоторых случаях вы можете не знать третий (или последующий) интерфейс сразу после создания объекта, поэтому вам может потребоваться сохранить доступ к IUnknown
в течение некоторого произвольного промежутка времени, прежде чем выпустить его.
Почему за вызовом QueryInterface всегда следует вызов Release?
Потому что QueryInterface будет вызывать AddRef, который увеличивает количество ссылок на указатель. При наличии 0 ссылок на указатель он освобождается для вас.
Примечание: в ответах на этот вопрос есть некоторая путаница относительно того, что QueryInterface
на самом деле Он просто извлекает указатели на поддерживаемые интерфейсы объекта и увеличивает счетчик ссылок на этот объект. Он не создает новый объект для каждого реализуемого интерфейса.
Например, если у вас есть объект, который реализует 2 интерфейса, то вызов просто приведёт этот объект к каждому интерфейсу и увеличит переменную, которая используется в качестве счетчика ссылок.
Примечание: подсчет ссылок может быть реализован по-разному, но вышеизложенное объясняет обычный сценарий. В частности, @Ben описывает отрывной интерфейс, ниже которого подчеркивается важность вызова Release для указателя интерфейса, который был вам возвращен.
Этот фрагмент кода, похоже, заинтересован только в получении значения ppv. Обратите внимание, что указатель интерфейса не освобождается. Класс CDecoder, по-видимому, является средством для его получения. Для его создания существует новый оператор, не являющийся стандартным способом COM для создания класса COM, который принимает CoCreateInstance(). Очевидно, для правильного использования этого класса требуется вызов Release() вместо использования оператора delete. Опять же, не совсем стандартно, но не невозможно. Я могу только догадываться, что CDecoder - это класс C++, который реализует COM-класс COM, и этот код использует его напрямую, а не через обычные процедуры COM.
Не думайте, что этот код является стандартным. Это совсем не так.