2009-02-11 3 views
1

Я всегда использовал следующее правило для подписей функций, возвращающих объекты, подсчитанные с помощью ref-counted, на основе того, делают ли они AddRef или нет, но хотят также объяснить это своим коллегам ... Так что мой вопрос является ли правило, описанное ниже широко используемым правилом? Я ищу указатели на (например) правила кодирования, которые пропагандируют этот стиль.AddRef и подпись функции


Если функция не добавляет ссылку на объект, он должен быть возвращен в качестве возвращаемого значения функции:

class MyClass 
{ 
protected: 
    IUnknown *getObj() { return m_obj; } 
private: 
    IUnknown *m_obj; 
}; 

Однако, если функция добавляет ссылку на объект, то указатель указатель на часть объекта передается в качестве параметра функции:

class MyClass 
{ 
public: 
    void getObj(IUnknown **outObj) { *outObj = m_obj; (*outObj)->AddRef(); } 
private: 
    IUnknown *m_obj; 
}; 

ответ

2

Это гораздо более типично для использования подсчета ссылок смарт-указатели в тех случаях, когда создается новый объект и вызывающий должен взять на себя ответственность за это.

+0

Спасибо за предложение, хотя это был не совсем тот ответ, который я искал. Я все еще работаю в темные века, я думаю ...;) – awm

+0

Вы можете делать много вещей с C++, но не все из них должны быть выполнены. В этом случае использование умного указателя обеспечивает большую ясность. В случае COM-возврата ничего, кроме HRESULT, не допускается, но здесь дело обстоит не так. – sharptooth

2

Я использовал этот же стиль для проектов с большим количеством COM. Мне учили несколько человек, которые узнали об этом, когда они работали в NuMega на маленькой вещи под названием SoftICE. Я думаю, что это также стиль, преподаваемый в книге «Essential COM», Дон Бокс (here it is at Amazon). В какой-то момент эта книга считалась Библией для COM. Я думаю, что единственная причина, по которой это не так, - это то, что COM стал намного больше, чем просто COM.

Все, что сказал, я предпочитаю CComPtr и другие умные указатели.

+0

Humm, звучит многообещающе ... Но я проверяю «Essential COM» на книгах Google, на стр. 54 он говорит, чтобы вызвать AddRef(), когда «возвращает указатель не нулевого интерфейса как физический результат функции». http://books.google.com/books?id=kfRWvKSePmAC&printsec=frontcover&dq=essential+com#PPA54,M1 – awm

+0

Он делает - но это неправильно! –

+0

awm: Представляющий интерес объект A2, касающийся параметров [out] и [in, out]. На следующей странице показан пример именно того стиля, который вы задали о том, где ppUnkPunk - возвращаемый счетчик возвращаемого объекта. (FYI ppX также является стандартной венгерской нотацией (визуальная подсказка) для указателя на указатель X.) – Mike

2

Один из подходов - никогда не использовать возвращаемое значение функции. Используйте только выходные параметры, как во втором случае. В любом случае это уже правило в опубликованных 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& вызывается, когда у него уже есть объект.

+0

Спасибо. Как вы говорите, умный указатель - это действительно правильный подход. И я думаю, что мой второй случай следует избегать, по крайней мере, для публичных интерфейсов ... – awm