Один из подходов - никогда не использовать возвращаемое значение функции. Используйте только выходные параметры, как во втором случае. В любом случае это уже правило в опубликованных COM-интерфейсах.
Вот «официальная» ссылка, но, как это обычно бывает, не говоря уже даже первый случай: http://support.microsoft.com/kb/104138
Но внутри компонента, запрещая возвращаемые значения делает уродливый код. Гораздо удобнее иметь возможность компоновки, т. Е. Удобно совмещать функции, передавая возвращаемое значение одной функции непосредственно в качестве аргумента другому.
Умные указатели позволяют это сделать. Они запрещены в общедоступных COM-интерфейсах, но тогда это также значения возврата, не относящиеся к HRESULT. Следовательно, ваша проблема уходит. Если вы хотите использовать возвращаемое значение для возврата указателя интерфейса, сделайте это с помощью умного указателя. И сохраните элементы в интеллектуальных указателях.
Однако предположим, что по какой-то причине вы не хотели использовать интеллектуальные указатели (вы, сумасшедший, кстати!), Тогда я могу сказать вам, что ваши рассуждения верны. Ваша функция действует как «свойство getter», и в вашем первом примере это не должно быть AddRef
.
Так ваше правило правильно (. Хотя есть ошибка в реализации которой я пришел в секунду, как вы, возможно, не заметили его)
Эта функция хочет объект:
void Foo(IUnknown *obj);
Он не должен влиять на obj
, если он не хочет хранить его в переменной-члене. Это обязательно должно быть NOT несет ответственность за Foo, чтобы позвонить Release
по телефону obj
, прежде чем он вернется! Представьте себе беспорядок, который будет создан.
Теперь эта функция возвращает объект:
IUnknown *Bar();
И очень часто мы любим создавать функции, передавая вывод одного непосредственно к другому:
Foo(Bar());
Это не будет работать, если Bar
было натолкнулся на пересчет того, что он вернул. Кто собирается Release
это? Таким образом, Bar
не звонит AddRef
. Это означает, что он возвращает что-то, что он хранит и управляет, т. Е. Это эффективно свойство getter.
Кроме того, если абонент использует смарт-указатель, p
:
p = Bar();
Любой здравомыслящий умный указатель будет AddRef
, когда он назначается объект. Если Bar
имел также AddRef
-хорошо, мы снова пропустили один счет. Это действительно просто частный случай той же проблемы с возможностью сложения.
Выходные параметры (указатель на указатель) различны, потому что они не страдают от этой проблемы компонуемости таким же образом:
Опять же, умные указатели обеспечивают наиболее общий случай, используя свой второй пример:
myClass.getObj(&p);
умный указатель не собирается делать какие-либо реф-подсчитывая здесь, так getObj
должен это сделать.
Теперь мы подошли к ошибке. Предположим, что смарт-указатель p
уже указывает на то, когда вы передаете его getObj
...
Исправленная версия:
void getObj(IUnknown **outObj)
{
if (*outObj != 0)
(*outObj)->Release();
*outObj = m_obj;
(*outObj)->AddRef(); // might want to check for 0 here also
}
На практике, люди делают эту ошибку так часто, что я считаю, что проще сделать мой smart pointer assert, если operator&
вызывается, когда у него уже есть объект.
Спасибо за предложение, хотя это был не совсем тот ответ, который я искал. Я все еще работаю в темные века, я думаю ...;) – awm
Вы можете делать много вещей с C++, но не все из них должны быть выполнены. В этом случае использование умного указателя обеспечивает большую ясность. В случае COM-возврата ничего, кроме HRESULT, не допускается, но здесь дело обстоит не так. – sharptooth