C primer 5-е издание: разница между удалением shared_ptr и unique_ptr-ов

#c #shared-ptr #unique-ptr

#c #разделяемый ptr #уникальный-ptr

Вопрос:

В «C Primer, 5-е издание» говорится, что тип удалителя a shared_ptr неизвестен до времени выполнения, поскольку удалитель хранится не непосредственно как элемент, а как указатель, который может указывать на удалитель. Тип удалителя в a unique_ptr известен во время компиляции, потому что он является частью самого unique_ptr себя.

Итак, я привел этот пример:

 #include <functional>

template <typename T>
struct SharedPtr
{
    void(*pDel_)(T*) = nullptr;
    T* ptr_{ new T{} };
    ~SharedPtr(){ pDel_ ? pDel_(ptr_) : delete ptr_; }
};

template <typename T, typename D = std::function<void(T*)>>
struct UniquePtr
{
    D pDel_;
    T* ptr_{ nullptr };
    UniquePtr(T* p = nullptr, D del = D{}) :
        ptr_(p), pDel_(del){}
    ~UniquePtr(){ pDel_(ptr_); }
};

int main()
{
    SharedPtr<int> spi{};
    cout << *spi.ptr_ << endl;
    UniquePtr<std::string> upd{new std::string("Helo!"),
        [](std::string* p){std::cout << "freeing memory...n"; delete p; }};

}
  
  • на мой взгляд, тип удаляемого SharedPtr элемента известен во время компиляции ( void(*)(T*) ), но значение — нет.

  • с другой стороны, тип deleter в UniquePtr действительно известен и во время компиляции, но опять же значение может и не быть.

  • Итак, в книге сказано, что:

 // del bound at compile time; direct call to the deleter is instantiated
del(p);   // no run-time overhead
The type of del is either the default deleter type or a user-supplied type. It doesn’t
matter; either way the code that will be executed is known at compile time. Indeed, if
the deleter is something like our DebugDelete class (§ 16.1.4, p. 672) this call might
even be inlined at compile time.
By binding the deleter at compile time, unique_ptr avoids the run-time cost of an
indirect call to its deleter. By binding the deleter at run time, shared_ptr makes it
easier for users to override the deleter.
  

Если параметр удаления в UniquePtr является указателем на функцию, но этот указатель есть nullptr , то del(p) он не определен.

Пожалуйста, помогите мне понять этот параграф. Я реализовал свой shared_ptr и unique_ptr только на практике.

Ответ №1:

на мой взгляд, тип удалителя в shared_ptr известен во время компиляции ( void(*)(T*) ), но значение — нет.

Это верно для вашего shared_ptr.

Это не относится к std::shared_ptr .

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

Когда вы создаете экземпляр UniquePtr с std::function<void(T*)> помощью, вы будете знать только этот тип во время компиляции, но не тип объекта функции, который им обернут.

std::unique_ptr по умолчанию не использует std::function deleter .

Если параметр удаления в UniquePtr является указателем на функцию, но этот указатель равен nullptr , тогда del(p) не определено.

Это правда. Не делайте этого.

Пожалуйста, помогите мне понять этот параграф.

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

Используйте удаление без состояния, например std::default_delete<T> (или, возможно, DebugDelete, упомянутый в цитате, который, предположительно, также не имеет состояния), чтобы получить преимущества во время компиляции.

Комментарии:

1. Вы имеете в виду, если я делаю что-то вроде: std::unique_ptr<int, std::function<void(int*)>> upi(new int{7}, [](int* p ){delete p;}); имеет те же издержки времени выполнения std::shared_ptr , что и у ‘s?

2. @Maestro В отношении времени компиляции удаления: по сути, да. Общий указатель имеет другие накладные расходы, помимо этого, такие как счетчик атомарных ссылок и т. Д.

3. ОК. Большое вам спасибо. Не могли бы вы порекомендовать какую-нибудь хорошую ссылку, чтобы прочитать, как реализованы интеллектуальные указатели?

4. @Maestro Я бы рекомендовал прочитать об удалении типов . Он применяется как std::function для удаления, так и std::shared_ptr для удаления.

5. @Maestro Вы можете прочитать источник для получения подробной информации. В документации Boost может быть что-то сказано, хотя, вероятно, в основном о том, как их использовать.