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

Я создаю оболочку для 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;
}

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

Ответы [ 5 ]

6 голосов
/ 20 июня 2010

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

void my_deleter(Foo *ptr)
{
 delete ptr;
 std::cout<< "using custom deleter" <<std::endl;
}
shared_ptr<Foo> pf (new Foo, my_deleter); 
5 голосов
/ 20 июня 2010

Если вам нужен доступ к нестатическим элементам из 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();

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

5 голосов
/ 20 июня 2010

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

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, если считаете, что это слишком многословно.

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

3 голосов
/ 20 июня 2010

Я не проверял это, но на основе идеи, представленной 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)); 
0 голосов
/ 20 июня 2010

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

...