Получение shared_ptr для вызова функции-члена, когда ее счетчик ссылок достигнет 0

Я создаю оболочку для HANDLE, которая не работает с DuplicateHandle, поэтому вместо этого я пытаюсь обернуть ручку в shared_ptr.

Представьте себе следующий код:

class CWrapper
{
public:
    CWrapper() :
        m_pHandle(new HANDLE, &CWrapper::Close)
    {
        //code to open handle
    }

private:
    void Close() 
    { 
        //code to close handle
    }

    std::shared_ptr<HANDLE> m_pHandle;
}

Я также пытался создать close с параметром HANDLE (не идеально). В любом случае, я получаю ошибку компилятора "Термин не оценивает функцию, принимающую 0 аргументов". Это из-за неявного указателя this? Как это исправить? Как вызвать функцию-член из общего указателя?

5 ответов

Решение

Я думаю, что ваши абстракции неправильно.

shared_ptr дает вам копируемый "дескриптор" к общему ресурсу, который сам не может быть скопирован. С помощью shared_ptr с типом, который не выполняет свою собственную очистку при удалении, не является оптимальным использованием.

Если вы возьмете на себя единственную ответственность вашего класса за то, чтобы правильно очистить этот изначально не копируемый ресурс в его деструкторе, вы можете использовать shared_ptr обеспечить совместное владение, что является его единственной обязанностью. (Я полагаю HANDLE быть не копируемым, как будто вы пытаетесь сделать простую копию HANDLE копии не могут рассматриваться как независимые; последняя копия должна быть правильно закрыта, чтобы владельцы копий должны были знать о других существующих копиях.)

class CWrapper
{
public:
    CWrapper()
    {
        // code to open handle
    }

    ~CWrapper()
    {
        // code to close handle
    }

private:
    // prevent copying
    CWrapper(const CWrapper&);
    CWrapper& operator=(const CWrapper&);

    HANDLE mHandle;
};

Сейчас использую shared_ptr<CWrapper> там, где вам нужно поделиться дескриптором, вы можете использовать typedef, если считаете, что это слишком многословно.

Пользовательское средство удаления - это слишком сложное решение, ИМХО.

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

void my_deleter(Foo *ptr)
{
 delete ptr;
 std::cout<< "using custom deleter" <<std::endl;
}
shared_ptr<Foo> pf (new Foo, my_deleter); 

Если вам нужен доступ к нестатичным членам изнутри Close вам нужно связать его this аргумент правильно

CWrapper() :
   m_pHandle(new HANDLE, boost::bind(&CWrapper::Close, this, _1)) {
    //code to open handle
}

Это, однако, содержит скрытую ошибку. Ваш объект является копируемым, и вы привязываете удалитель к объекту *this, Дескриптор связан с первой созданной вами оболочкой, но если вы скопируете оболочку, дескриптор будет общим, но связанным с первой оболочкой, которая может больше не существовать:

CWrapper getWrapper() { CWrapper w; return w;  }
CWrapper x = getWrapper();

После того, как этот код был выполнен и x будет уничтожен, поведение не определено, потому что xуничтожение внутреннего указателя дескриптора будет пытаться использовать объект, связанный в wвызов конструктора - однако этот объект больше не существует!

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

class CWrapper
{
public:
  CWrapper():m_pHandle(new CHandle)
  { }

private:
    // This class cannot be copied
    class CHandle : boost::noncopyable {
      friend class CWrapper;

      CHandle() 
        :m_pHandle(new HANDLE) {
          // code to open handle
      }

      ~CHandle() {
        // code to close this handle, making use of 
        // auxilary data for whatever reason
      }

    private:
      boost::scoped_ptr<HANDLE> m_pHandle;
      // auxilary data associated with the handle...
    };

    boost::shared_ptr<CHandle> m_pHandle;
};

Вспомогательные данные больше не хранятся в дескрипторе, но вместе с данными, которые совместно используются всеми копиями обертки. Совместно используемые данные создаются и уничтожаются с помощью обычного конструктора и деструктора.

CWrapper getHandle() { return myHandle; }
CWrapper w = getHandle();

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

Я не проверял это, но на основе идеи, представленной shoosh, вы можете передать функцию-член следующим образом:

void Class::myDeleteMember(Foo *ptr)
{
 delete ptr;
 std::cout<< "using custom deleter" <<std::endl;
}
shared_ptr<Foo> pf (new Foo, boost::bind(&Class::myDeleteMember, _1)); 

Ищу deleter в boost::shared_ptr документы. Я не смог найти прямую ссылку на него, но в основном это функтор, который вызывается, когда ref равен 0.

Другие вопросы по тегам