Подтвердить что ты не робот

Каковы правила для конверсий между различными интеллектуальными указателями в С++

В TR1 были введены shared_ptr, weak_ptr, scoped_ptr и unique_ptr и др.

Я хотел бы знать различные правила преобразования/типа продвижения между этими типами.

Например, что происходит, когда scoped_ptr назначается shared_ptr? Являются ли такие преобразования возможными/значимыми и каковы некоторые прецеденты для таких преобразований?

(Есть ли таблица в спецификации?)

4b9b3361

Ответ 1

Во-первых, несколько поправок к вашему вопросу:

  • scoped_ptr является частью Boost и не входит ни в С++ TR1, ни в С++ 0x (ожидается, что в С++ 0x unique_ptr можно использовать, где scoped_ptr традиционно используется).

  • unique_ptr не является частью С++ TR1; он является частью С++ 0x (поскольку он полагается на ссылки rvalue и перемещает семантику, которые доступны только в С++ 0x).

Чтобы ответить на ваш вопрос: shared_ptr и weak_ptr идут рука об руку. Объект, принадлежащий shared_ptr, также может ссылаться на weak_ptr. Они дополняют друг друга.

A unique_ptr имеет единоличную собственность на объект, которым он управляет; никто другой не может владеть объектом. Это противоположно семантике собственности shared_ptr: при unique_ptr у вас есть уникальное уникальное право собственности; с shared_ptr, которым вы поделились, не уникальное право собственности.

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

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

Ответ 2

Для двух классов A и B (которые могут быть типами интеллектуальных указателей) существует четыре основных способа преобразования экземпляра типа B в тип A:

  • A - это доступный объект базового класса B (например, B общедоступен из A), и преобразование может разрезать или просто отрегулировать тип ссылки или указатель. (преднамеренное протаскивание).

  • A имеет доступный конструктор с B.

  • B имеет доступный оператор преобразования, создающий A.

  • Существует некоторая функция, которая принимает B и создает A, и вы вызываете эту функцию.

Для интеллектуальных указателей наследование не используется для облегчения преобразования, поскольку наследование допускает неправильные преобразования; следовательно, пробивать выше. Например, если SmartPtr<Derived> наследуется публично из SmartPtr<Base>, тогда можно было бы сделать SmartPtr<Base>& spBase = spDerived;, а затем, например, spBase = spOtherDerived, что было бы довольно проблематично... При достаточно высоком уровне абстракции это по существу та же проблема, что и для const для конверсий указателей; см. раздел "Вопросы и ответы" 18.17 "Почему я получаю сообщение об ошибке с преобразованием Foo ** → Foo const **?" .

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

По существу в С++ 0x есть три умных указателя, не считая устаревшего auto_ptr:

  • std::unique_ptr для отдельных объектов.

  • std::unique_ptr для массивов.

  • std::shared_ptr для отдельных объектов.

unique_ptr выражает передачу прав собственности, как это делал и делает старый auto_ptr. Но auto_ptr не поддерживал массивы. unique_ptr, и это влияет на возможные преобразования.

Для отдельных объектов unique_ptr поддерживает преобразования, которые выполняют соответствующие указатели на raw, через конструкторы. У него есть шаблонный конструктор, принимающий unique_ptr другого типа. См. С++ 0x черновик N3126 §20.9.10.2.

Но для массивов, которые были бы столь же опасны, как и для сырых указателей! И поэтому для массивов unique_ptr не предлагается базовое/производное преобразование. См. С++ 0x черновик N3126 §20.9.10.3.

Так как unique_ptr выражает передачу прав, тогда как shared_ptr выраженное совместное владение не может быть безопасным общим преобразованием от shared_ptr до unique_ptr. Однако, в противном случае Boost shared_ptr имеет конструктор, принимающий auto_ptr, а С++ 0x shared_ptr сохраняет это (также имеет его) и, естественно, добавляет конструктор, принимающий unique_ptr. См. С++ 0x черновик N3126 §20.9.11.2/1.

shared_ptr предоставляет базовые/производные преобразования через конструкторы и через свободные функции, которые концептуально реализуют "отливки". По существу это означает, что shared_ptr довольно опасно использовать непосредственно для массивов объектов типа класса. Для этого используйте его.

Преобразование из shared_ptr в unique_ptr, как уже упоминалось, не поддерживается как общая операция. Поскольку совместное владение напрямую не совместимо с передачей прав собственности. Однако, невзирая на осложнения, безопасность потока, shared_ptr::unique сообщает вам, есть ли один владелец (а именно ваш экземпляр), а затем, если у вас есть необходимое знание о том, как был создан начальный shared_ptr, вы можете использовать свободную функцию get_deleter для получения указатель на функцию делетера и сделать некоторые махинации низкого уровня. Если вы полностью поймете, о чем я говорю здесь, тогда все хорошо, хорошо. Если нет, то лучше всего, что я не даю более подробных сведений, потому что это очень особый случай, и он требует максимальной осторожности и что вы действительно знаете, что делаете и торгуете;.; -)

Хорошо, что об этом. Я не обсуждаю weak_ptr, так как это только часть функциональности shared_ptr. Но, надеюсь, это то, о чем вы просите.

Ответ 3

  • scoped_ptr AFAIK не входит в TR1 (исправьте меня, если я ошибаюсь). Обычно boost scoped_ptr не может передавать право собственности. После того, как вы указали на него указатель, вы не сможете его освободить.
  • unique_ptr может передавать право собственности только с помощью std::move, чтобы он не передавал право собственности.
  • shared_ptr не может освободить право собственности, поскольку другие указатели могут поделиться им. Его можно преобразовать в weak_ptr. Если вы попытаетесь преобразовать его из weak_ptr, и объект был выпущен, он будет бросать.
  • weak_ptr может быть создан из shared_ptr, при преобразовании в shared_ptr он может выкинуть, если объект больше не существует.