Разумно ли предоставлять доступ к weak_ptr в интерфейсе библиотеки?

StackOverflow https://stackoverflow.com/questions/631380

  •  08-07-2019
  •  | 
  •  

Вопрос

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

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

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

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

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

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

Это было полезно?

Решение

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

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

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

Другие советы

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

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

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

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

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

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

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

Если вы хотите отловить недопустимое использование библиотеки (пытаясь получить доступ к объектам, когда они были удалены), а также иметь высокопроизводительный 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 не поможет, если у вас есть " stupid " пользователи API, потому что они могли бы вызвать lock () и затем сделать ссылку на нулевой указатель после того, как weak_ptr вернет shared_ptr, который пуст ...

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top