Это допустимое использование intrusive_ptr? - PullRequest
4 голосов
/ 19 апреля 2011

В моем коде я придерживаюсь двух правил, когда дело доходит до intrusive_ptrs:

  • Передача необработанного указателя по значению подразумевает, что необработанный указатель гарантированно будет действительным в течение времени жизни этой функции.
  • Если необработанный указатель должен храниться и использоваться после срока действия функции, он должен храниться в intrusive_ptr.

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

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

Ответы [ 2 ]

9 голосов
/ 19 апреля 2011

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

Передача необработанного указателя в частном API, например у членов одного класса проблем нет.

Рассмотрим эти три функции:

void f(A* a);
void g(std::unique_ptr<A> a);
void h(std::shared_ptr<A> a);

Семантика владения f не ясна. Если вы являетесь клиентом f, вам необходимо прочитать документацию, чтобы выяснить, собирается ли f освободить a, или игнорировать проблемы владения для a.

Семантика владения g ясна. Когда вы звоните g, вы передаете право владения a g и больше не несете за него ответственность. g либо освободит a, либо передаст право собственности на этот ресурс в другое место.

Семантика владения h ясна. Когда вы звоните h, вы и h становитесь совладельцами a. Последний выключает свет.

void q(boost::intrusive_ptr<A> a);

q имеет ту же семантику владения, что и h. Основное отличие состоит в том, что должны существовать следующие свободные функции:

intrusive_ptr_add_ref(A*);
intrusive_ptr_release(A*);

Если вы являетесь автором f и вызываете эти функции на a, вам следует документально подтвердить это. Ваши клиенты не обязательно будут знать, что вы есть. И если вы клиент f, у вас нет возможности узнать, вызовет ли f эти функции, если вы не прочитаете его документацию.

Если вы являетесь автором f и намереваетесь вызывать функции intrusive_ptr_*, вы можете сделать это явным образом в своем интерфейсе, кодируя вместо него q.

Но, как правило, нет веской причины навязывать автору A написание функций intrusive_ptr_*. И вы можете получить ту же семантику владения, что и q, написав вместо этого h, не налагая каких-либо дополнительных требований на A.

Нагрузка на память

Если вы создаете shared_ptr с помощью:

 shared_ptr<A> p = make_shared(arguments-to-construct-an-A);

тогда ваш shared_ptr будет иметь те же накладные расходы памяти, что и intrusive_ptr. Реализация выделит A и refcount в том же распределении памяти. Ваши клиенты не должны знать или заботиться о том, что ваш shared_ptr был построен так эффективно.

2 голосов
/ 19 апреля 2011

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

Кроме того: проверяли ли вы когда-нибудь, что накладные расходы на копирование умных указателей действительно влияют на ваше приложение? Я предпочел бы сначала перейти к простой версии и вводить intrusive_ptr только тогда, когда это действительно необходимо.

...