AddRef и подпись функции - PullRequest
       11

AddRef и подпись функции

1 голос
/ 11 февраля 2009

Я всегда использовал следующее правило для сигнатур функций, которые возвращают объекты с пересчетом в зависимости от того, выполняют ли они 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;
};

Ответы [ 3 ]

2 голосов
/ 11 февраля 2009

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

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

2 голосов
/ 11 февраля 2009

Один из подходов - никогда не использовать возвращаемое значение функции. Используйте только выходные параметры, как во втором случае. В любом случае это уже правило в опубликованных интерфейсах COM.

Вот "официальная" ссылка, но, как обычно, она даже не упоминает ваш первый случай: http://support.microsoft.com/kb/104138

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

Умные указатели позволяют вам это делать. Они запрещены в общедоступных интерфейсах COM, но также и возвращаемые значения, отличные от HRESULT. Следовательно, ваша проблема исчезнет. Если вы хотите использовать возвращаемое значение для передачи указателя интерфейса, сделайте это с помощью умного указателя. И сохраняйте членов в умных указателях.

Однако, если по какой-то причине вы не хотите использовать умные указатели (кстати, вы сумасшедшие!), Тогда я могу сказать вам, что ваши рассуждения верны. Ваша функция действует как «свойство getter», и в вашем первом примере она не должна AddRef.

Так что ваше правило верно (хотя в вашей реализации есть ошибка, о которой я сообщу через секунду, поскольку вы, возможно, ее не заметили).

Эта функция хочет объект:

void Foo(IUnknown *obj);

Это вообще не должно влиять на refcount obj, если только он не хочет сохранить его в переменной-члене. Разумеется, НЕ будет обязанностью Foo позвонить Release на obj до его возвращения! Представьте себе беспорядок, который может создать.

Теперь эта функция возвращает объект:

IUnknown *Bar();

И очень часто нам нравится составлять функции, передавая выходные данные одного непосредственно другому:

Foo(Bar());

Это не сработало бы, если бы Bar увеличил счет возврата того, что вернул. Кто собирается Release это? Так что Bar не звонит AddRef. Это означает, что он возвращает что-то, что хранит и управляет им, т. Е. Фактически он получает объект.

Также, если вызывающий абонент использует интеллектуальный указатель, 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
}

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

2 голосов
/ 11 февраля 2009

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

...