Разумно ли предоставлять доступ к weak_ptr в интерфейсе библиотеки? - PullRequest
4 голосов
/ 10 марта 2009

Я написал библиотеку, которая предоставляет ссылки на несколько связанных типов объектов. Время жизни всех этих объектов управляется библиотекой внутренне через boost::shared_ptr

Пользователь библиотеки также мог бы знать, по природе библиотеки, время жизни любого из экспонированных объектов. Чтобы они могли хранить указатели или хранить ссылки на эти объекты. Для них было бы разумно сделать это и знать, когда эти объекты больше не действительны.

Но я чувствую вину, заставляя моих пользователей быть разумными.

Допустимо ли, чтобы библиотека выставляла weak_ptr на свои объекты? Другие библиотеки сделали это?

Я описал использование этой библиотеки в приложениях и обнаружил, что она слишком критична для предоставления доступа исключительно к weak_ptr.

Разумнее было бы, чтобы соответствующие функции API предоставляли ссылку или слабый_птр или чтобы любой объект мог выставлять weak_ptr сам по себе?

Ответы [ 5 ]

6 голосов
/ 15 марта 2009

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

В этом случае я бы рекомендовал каждому объекту выдавать weak_ptr s в дополнение к любому другому доступу, который предлагает ваша библиотека. Это дает пользователям максимальную гибкость: если им нужен weak_ptr, они получают немедленный доступ к нему; если им нужен shared_ptr, они могут легко получить его; и если им просто нужен доступ к самому объекту, они могут полностью игнорировать умные указатели.

Конечно, я не знаю, что делает ваша библиотека или как она используется или разработана. Это может изменить мою рекомендацию.

4 голосов
/ 18 марта 2009

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

Нельзя проектировать вокруг плохих / неопытных пользователей.

2 голосов
/ 17 марта 2009

Если вы предоставляете своим клиентам доступ к weak_ptr s, они могут просто заблокировать их для создания shared_ptr s и в конечном итоге отложить уничтожение объектов. Это может вызвать проблемы с вашей библиотекой.

Я предлагаю обернуть weak_ptr в другой класс и дать вызывающему shared_ptr для этого. Таким образом, они не могут просто позвонить weak_ptr<T>::lock(). Кажется, у вас есть ограничения производительности, которые могут повлиять на то, как вы его реализуете, но shared_ptr<InterfaceClass> может быть хорошим способом пойти и сохранить класс с weak_ptr внутренним для вашей библиотеки.

Таким образом, вы также скрываете эти детали реализации от интерфейса вашей библиотеки и можете изменить способ его реализации без изменения интерфейса.

1 голос
/ 10 марта 2009

Я не вижу никаких проблем с раскрытием уязвимых мест, особенно если учесть, что TR1 имеет похожие умные указатели (PDF).

TR1 в значительной степени реализован Visual Studio и GCC, но не некоторыми другими компиляторами. Но когда он реализован во всех нужных вам компиляторах, вы можете переделать API, чтобы вместо этого использовать эти умные указатели.

0 голосов
/ 21 марта 2009

Если вы хотите отловить недопустимое использование библиотеки (пытаясь получить доступ к объектам, когда они были удалены), а также иметь высокопроизводительный API-интерфейс (без API-адресов weak_ptr и shared_ptr), вы можете рассмотреть возможность использования другой API для отладочных и не отладочных сборок.

Давайте для простоты предположим, что у вас есть только один класс объектов, которые вы выставляете; Назовите этот класс Object. Тип указателя, который вы возвращаете из API для доступа к внутренним объектам, определяется как:

#ifdef DEBUG
typedef ObjectPtrFacade ObjectPtr 
#else
typedef Object * ObjectPtr;
#endif

Здесь фасад - это класс, который ты пишешь. Это работает примерно так:

class ObjectPtrFacade {
public:
    ObjectPtrFacade(Object *o) : wptr(o) { }
    // copy constructor and assignment here etc. (not written)
    Object * operator -> () const { return access(); }
    Object & operator * () const { return *access(); }
private:
    Object * access() { 
      assert(wptr.use_count() > 0);
      return (Object *)(wptr.lock());
    }
    weak_ptr<Object> wptr; 
}

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

В общем, конечно, это так, что использование weak_ptr не помогает, если у вас есть «глупые» пользователи API, потому что они могут вызвать lock () и затем сделать ссылку на нулевой указатель после того, как weak_ptr вернет shared_ptr, пусто ...

...