C++ обёртка для HANDLE
Oct 31, 2006 · CommentsC/C++Программирование
Пару лет назад я пользовался вот такой C++ обёрткой для HANDLE
(часть методов и обработка ошибок опущены):
/// @brief A generic policy-based wrapper for a handle value
template <typename T, typename PolicyT>
class
{
public:
/// @brief Construct a wrapper from a raw value
HandleT(const T& _handle = PolicyT::null()):
m_handle(_handle)
{}
/// @brief Duplicate a handle
HandleT(const HandleT& _right):
m_handle(PolicyT::copy(_right.get()))
{}
~HandleT()
{
release();
}
/// @brief Duplicate a handle
HandleT& operator=(const HandleT& _right)
{
release();
m_handle = PolicyT::copy(_right.get());
return *this;
}
/// @brief Close the enclosed handle
void
release()
{
PolicyT::release(m_handle);
m_handle = PolicyT::null();
}
private:
T m_handle;
};
Идея была – написать класс, который:
-
Автоматически освобождает ресурсы при выходе из области видимости;
-
Обеспечивает легкость копирования описателей;
-
Умеет работать с различными типами описателей.
Чтобы удовлетворить третий пункт базовые операции были вынесены в отдельные классы “политик” (policy classes). Политика стандартного Win32 HANDLE
выглядела так (обработка ошибок опущена):
/// @brief Handle pocily for Win32 HANDLE
struct
{
static HANDLE null()
{
return NULL;
}
/// @brief Release a handle
static void release(const HANDLE& _handle)
{
if (_handle)
{
::CloseHandle(_handle);
}
}
/// @brief Make a copy of the given handle
static HANDLE copy(const HANDLE& _handle)
{
HANDLE retval = NULL;
if (_handle != NULL)
{
DuplicateHandle(
GetCurrentProcess(),
_handle,
GetCurrentProcess(),
&retval,
0,
FALSE,
DUPLICATE_SAME_ACCESS);
}
return retval;
}
};
И, наконец, тип класса обертки объявлялся так:
/// @brief A wrapper for Win32 HANDLE
typedef HandleT<HANDLE, HandlePolicy> Handle;
Таким образом, класс Handle
соответствовал двум оставшимся требованиям и был, надо сказать, весьма удобен в использовании. Тем не менее, эта реализация обладала одним существенным недостатком. Каким? Для копирования описателя использовался функция DuplicateHandle
. Что не так с DuplicateHandle
? DuplicateHandle
- слишком мощная и тяжеловесная операция для простого копирования описателя. С++ класс, по определению, используется в рамках одного приложения. DuplicateHandle
– умеет копировать описатели между процессами и в силу этого её реализация относительно сложна. Описатели процесса организованы в таблицу и добавление описателя в таблицу довольно дорогая операция. Не говоря уже о том, что DuplicateHandle
делает вызов в ядро, что тоже не добавляет скорости. В результате, приложение, интенсивно копирующее описатели, генерирует многочисленные вызовы DuplicateHandle
, которые, в общем-то, не нужны.
Каким образом это можно исправить? Довольно легко. Достаточно вместо HANDLE
использовать “умный” указатель на HANDLE
со счётчиком ссылок. Тогда копирование описателя будет эквивалентно копированию указателя - т.е. копированию 4 или 8 байт и инкременту счетчика.