Как предотвратить удаление px.get () для unique_ptr

На веб-сайте boost.org я видел пример предотвращения удаления px.get () для shared_ptr (http://www.boost.org/doc/libs/1_51_0/libs/smart_ptr/sp_techniques.html#prevent_delete).

Это хороший метод, и я хотел бы применить его с помощью std :: unique_ptr в C ++ 11, и после некоторого изучения инструментов я не могу полностью заставить их пример работать с std :: unique_ptr.

Можно ли предотвратить вызов delete px.get () для std :: unique_ptr?

Вот код с веб-сайта boost.org, показывающий, как предотвратить вызов delete px.get:

class X
{
private:

    ~X();

    class deleter;
    friend class deleter;

    class deleter
    {
    public:

        void operator()(X * p) { delete p; }
    };

public:

    static shared_ptr<X> create()
    {
        shared_ptr<X> px(new X, X::deleter());
        return px;
    }
};

person AhiyaHiya    schedule 27.09.2012    source источник
comment
Совет: не вызывайте delete по указателю, полученному от std::unique_ptr::get!   -  person Christian Rau    schedule 27.09.2012
comment
Я бы воздержался от предотвращения этого, чтобы человек, который это делает, мог понять, что к нему приходит.   -  person GManNickG    schedule 27.09.2012


Ответы (2)


Идея остается той же для unique_ptr, за исключением того факта, что тип удалителя является частью типа unique_ptr.

#include <functional>
#include <memory>
#include <iostream>

using namespace std;

class X
{
private:

  ~X() {}

  class deleter
  {
  public:

    void operator()(X * p) { delete p; }
  };
  friend class deleter;

public:

  static shared_ptr<X> create_shared()
  {
    shared_ptr<X> px(new X, X::deleter());
    return px;
  }

  static unique_ptr<X, void(*)(X*)> create_unique()
  {
    return unique_ptr<X, void(*)(X*)>( new X, []( X *x ) { 
                                                  X::deleter()( x ); 
                                                } );
  }

  // If using VS2010
  static unique_ptr<X, std::function<void(X*)>> create_unique()
  {
    return unique_ptr<X, std::function<void(X*)>>( new X, X::deleter() );
  }
};

int main()
{
  auto x = X::create_shared();
  auto y = X::create_unique();
}

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

person Praetorian    schedule 27.09.2012

Хотя вызов delete px.get() - довольно глупая вещь, обычно вы не пытаетесь этого избежать, потому что это не то, что вы можете легко сделать случайно. Тем не менее, вот как исправить это с помощью unique_ptr, однако это уродливо, потому что тип удалителя просачивается в тип unique_ptr (почему это так, становится ясно, когда вы смотрите на детали реализации unique_ptr против shared_ptr) и, следовательно, делетер должен быть общедоступным, а это означает, что решение даже не является водонепроницаемым, потому что любой может использовать делетер. Если вы не сделаете конструктор удалителя закрытым и не сделаете X его другом, вы также должны задать себе вопрос: зачем беспокоиться? Кто-то должен намеренно поступить неправильно, написав delete px.get(), чтобы это превратилось в проблему.

class X
{
private:
   ~X() {}

public:
    struct deleter
    {
        void operator()(X * p) { delete p; }
    };

    static std::unique_ptr<X, deleter> create()
    {
      std::unique_ptr<X, deleter> px(new X, deleter());
      return px;
    }
};
person Seg Fault    schedule 27.09.2012
comment
Обычно это не то, что вы пытаетесь предотвратить, потому что это не то, что вы можете легко сделать случайно. Никто не звонит delete px.get(). Что они делают, так это получают голый указатель на объект, прикрепляют его на какое-то время, а затем забывают, что он им не принадлежит, и пытаются его удалить. Это, по крайней мере, пытается сделать это ошибкой компиляции. Кроме того, удалитель должен быть частным. - person Nicol Bolas; 27.09.2012
comment
@NicolBolas Когда вы передаете простые указатели на объекты, которые управляются интеллектуальными указателями, вы должны делать это в форме ссылок. - person Seg Fault; 27.09.2012
comment
Вам не нужно делать средство удаления общедоступным (это сводит на нет всю цель этого вопроса). Даже если вы включите удалитель в тип unique_ptr, как вы это сделали, пользователь всегда может использовать auto для создания экземпляра an, он просто не сможет указать тип явно. Другой способ - не включать удалитель в тип, а добавить абстракцию, как в моем ответе. - person Praetorian; 27.09.2012
comment
@SegFault: если вы не можете использовать ссылки, потому что вы можете передать NULL. - person Nicol Bolas; 27.09.2012
comment
Хорошо, похоже, нам понадобится дополнительная информация. Каковы именно варианты использования простых указателей, извлеченных из управляемого указателя? От чего именно мы пытаемся защититься, от глупости и / или случайного удаления в устаревшем коде? ... - person Seg Fault; 27.09.2012
comment
@ Prætorian Я думал о auto, но это будет зависеть от того, как используются указатели. Я не думал о том, чтобы скрыть тип удалителя, как вы, хорошая идея, хотя, вероятно, с некоторыми накладными расходами. - person Seg Fault; 27.09.2012
comment
Только метод std::function будет иметь накладные расходы, другой должен быть таким же, как включение удаления в тип. - person Praetorian; 27.09.2012
comment
Ах, я пропустил изменение стандарта, которое позволяет переносимо использовать лямбда-выражение с пустым списком захвата, например указателем на функцию. - person Seg Fault; 27.09.2012
comment
Одна из причин блокировки доступа к деструктору класса - это разработчики, с которыми я работаю. К сожалению, не все из них понимают shared_ptr и почему ужасно вызывать delete px.get (). Эти же разработчики также сделают const_cast для объекта, который не следует изменять, но это обсуждение для отдельного потока. - person AhiyaHiya; 27.09.2012