#c #standard-library #allocator
#c #стандартная библиотека #распределитель
Вопрос:
У меня есть приложение Visual Studio 2008 на C , в котором я внедряю замену стандартному распределителю, используемому в контейнерах типа std::vector
. Но я столкнулся с проблемой. Моя реализация основана на том, что распределителю принадлежит дескриптор ресурса. В случае, когда используется rebind
функция, мне нужно было бы передать право собственности на дескриптор новому распределителю. Что-то вроде этого:
template< class T >
class MyAllocator
{
public:
template< class U >
explicit MyAllocator( const MyAllocator< U >amp; other ) throw()
: h_( other.Detach() ) // can't do this to a `const`
{
};
// ...
private:
HANDLE Detach()
{
HANDLE h = h_;
h_ = NULL;
return h;
};
HANDLE h_;
}; // class MyAllocator
К сожалению, я не могу освободить старый распределитель от права собственности на дескриптор, потому что это const
. Если я удалю const
из конструктора повторной привязки, контейнеры не примут это.
error C2558: class 'MyAllocator<T>' : no copy constructor available or copy constructor is declared 'explicit'
Есть ли хороший способ обойти эту проблему?
Комментарии:
1. Разве распределители не обязаны не иметь состояния?? Где-то есть классическая статья Мэтта Остерна о распределителях, IIRC, но я не могу ее сейчас найти…
2. Вот статья: http://drdobbs.com/cpp/184403759 . И при беглом взгляде на это обсуждение кажется, что распределители с отслеживанием состояния действительно поддерживаются, хотя поддержка реализаций std lib раньше была проблемой.
3. @sbi — Спасибо вам за статью и обсуждение. У меня собирался возникнуть следующий вопрос о том, как обращаться
std::swap
.
Ответ №1:
Не имея особых знаний о распределителях (они никогда не были нужны): Ваш ctor для копирования принимает const
ссылку, тем самым обещая не изменять other
объект, но вы все равно пытаетесь его изменить. Хотя есть случаи, когда классы были разработаны таким образом ( std::auto_ptr
), это действительно кажется подозрительным.
Синтаксически вы всегда можете объявить h_
mutable
и создать Detach()
const
функцию-член, но я бы серьезно усомнился в семантике этой настройки, прежде чем пробиваться сквозь синтаксические джунгли с помощью палаша.
Ответ №2:
Что произойдет, если вы объявите h_
как mutable
?
Ответ №3:
Вы можете решить эту проблему с помощью дополнительного уровня косвенности, но это не идеальное решение. В принципе, ваш распределитель будет иметь указатель на дескриптор, который будет выделен / освобожден в конструкторе / деструкторе. Дескриптор, на который он указывает, повсюду будет неконстантным, поэтому вы можете «переместить» дескриптор из одного распределителя в другой. Однако это добавляет некоторые накладные расходы распределителю.
Я не знаю вашего точного случая, но кажется, что распределитель с сохранением состояния, который нетривиально копируется, должен быть тщательно рассмотрен со всеми его последствиями. Есть ли альтернативный способ упростить его дизайн, чтобы у него не было дескриптора только для перемещения?
Ответ №4:
Вы не можете передать право собственности, потому что распределитель может быть скопирован и восстановлен несколько раз даже в одном контейнере, и результирующие экземпляры будут использоваться одновременно.
Вместо этого вам придется совместно использовать ресурс. Создайте косвенное обращение к ресурсу с подсчетом ссылок. Что-то вроде:
class SharedHandle {
HANDLE h_;
int count;
SharedHandle(HANDLE h) : h_(h), count(1) {}
~SharedHandle() { CloseHandle(h_); } // or whatever to release the resource.
SharedHandle *Ref() { count; return this; }
void Unref() { if(!--count) delete this; }
}
и чем:
explicit MyAllocator( const MyAllocator< U >amp; other ) throw()
: h_( other.h_->Ref() )
В дополнение к контейнерам, которым, естественно, необходимо выделять разнородные блоки, такие как hash_map
/ unordered_map
, известно, что реализация контейнеров Microsoft выделяет различные странные вещи. Когда я отслеживал распределения в одном приложении Windows, откуда-то изнутри STL поступало много распределений странных размеров.