У меня есть вопрос о классах STL и распределителях, которые, кажется, не так легко найти в Интернете. Кто-нибудь знает, какой распределитель используется во вложенном классе STL? Например:
typedef std::vector<int> myvect;
// строка ниже была отредактирована, как указано в последующих ответах / комментариях
typedef std::map<int, myvect, std::less<int>, A> mymap; //uses my custom allocator for map creation
Давайте назовем распределитель по умолчанию D
и предположим, что у меня есть собственный распределитель A
,
Что бы произошло, если бы я сделал следующее:
Создать карту:
mymap mapInstance;
Теперь, предполагая, что запись существует для mapInstance[0]
Предположим, я вставил значение в вектор:
mapInstance[0].push_back(999);
Какой распределитель используется для динамической памяти вектора mapInstance[0]
?
Насколько я понимаю, что по умолчанию распределитель D
используется, но я хочу подтвердить, что пользовательский распределитель A
, который был передан на карту, вместо этого не используется. (Насколько я знаю, это произошло бы, если бы я использовал какой-то вариант вложенного размещения.)
Я понимаю, конечно, что метаданные / информация заголовка для mapInstance[0]
выделяется с помощью пользовательского распределителя A
, Что меня беспокоит, так это динамический часть памяти, то есть часть после d_dataBegin
,
Ваш вопрос связан с Модель распределителя, Это стиль дизайна распределителя, который автоматически распространяет распределитель контейнера на элементы контейнера, так что вы можете гарантировать, что все элементы контейнера распределены из одного и того же распределителя. Подробнее об этом ниже.
Чтобы ответить на ваш вопрос:
1) Контейнеры не используют модель распределителя по умолчанию, вы должны запросить ее явно (см. scoped_allocator_adaptor
ниже)
2) Ваш вложенный контейнер имеет тип std::vector<int>
это означает, что он использует по умолчанию std::allocator<int>
распределитель, и все экземпляры этого типа равны, поэтому ответ на ваш вопрос заключается в том, что он использует стандартный распределитель — не важно, какой, потому что каждый std::allocator<int>
та же.
Остальная часть этого ответа — только мысленный эксперимент, ответ на ваш вопрос выше: vector<int>
всегда использования std::allocator<int>
Теперь, если ваш вложенный тип был std::vector<int, A1<int>>
где A1<int>
это пользовательский распределитель, вопрос становится более интересным.
Вложенный контейнер будет использовать распределитель, с которым он построен, и вы не показали этого, потому что сказали «при условии, что запись существует для mapInstance[0]
«и как эта запись создается, это то, что определяет, какой распределитель он будет использовать.
Если эта запись создана следующим образом:
mapInstance[0];
тогда запись построена по умолчанию и будет использовать конструкцию по умолчанию A1<int>
,
Если эта запись создана следующим образом:
A1<int> a1( /* args */ );
myvect v(a1)
mapInstance.insert(mymap::value_type(0, v));
тогда запись будет копией v
и в C ++ 03 его распределитель будет копией v.get_allocator()
, но в C ++ 11 его распределитель будет копией std::allocator_traits<A1<int>>::select_on_container_copy_construction(v.get_allocator())
, который может быть быть копией v.get_allocator()
но может быть что-то другое (например, построенный по умолчанию A1
.)
(В C ++ 03 распределитель записи не может быть изменен после того, как он создан, поэтому ответ здесь заканчивается, но в C ++ 11 его можно заменить. Я предполагаю, что мы говорим о C ++ 11 до конца этого вопроса потому что распределители не очень интересны в C ++ 03.)
Если эта запись изменена следующим образом:
A1<int> a1( /* args */ );
myvect v(a1)
mapInstance[0] = v;
тогда вектор получает копию, назначенную может быть заменить распределитель, в зависимости от значения std::allocator_traits<A1<int>>::propagate_on_container_copy_assignment::value
Если эта запись изменена следующим образом:
A1<int> a1( /* args */ );
mapInstance[0] = myvect(a1);
тогда вектор получает перемещение, которому может быть заменить распределитель, в зависимости от значения std::allocator_traits<A1<int>>::propagate_on_container_move_assignment::value
Если эта запись изменена следующим образом:
A1<int> a1( /* args */ );
myvect v(a1)
swap( mapInstance[0], v );
тогда вектор поменяется местами, что может быть заменить распределитель, в зависимости от значения std::allocator_traits<A1<int>>::propagate_on_container_swap::value
Сейчас если A
является std::scoped_allocator_adaptor<A1<std::pair<const int, myvect>>>
все становится еще интереснее! scoped_allocator_adaptor
это, как следует из его названия, адаптер, который позволяет использовать любой тип распределителя с Модель распределителя это означает, что распределитель контейнера может быть передан дочерним элементам контейнера, его дочерним дочерним элементам и т. д. (при условии, что эти типы используют распределители и могут создаваться с ними).
По умолчанию контейнеры и распределители делают не использовать модель распределителя вам нужно использовать scoped_allocator_adaptor
(или напишите свой собственный тип распределителя, который работает так же), чтобы использовать его. (А в C ++ 03 вообще нет поддержки для распределителей области действия.)
Если эта запись создана следующим образом:
mapInstance[0];
затем вместо записи, созданной по умолчанию, scoped_allocator_adaptor
создаст его с копией распределителя карты, поэтому запись будет построена следующим образом myvect( A1<int>(mapInstance.get_allocator()) )
,
Если эта запись создана следующим образом:
A1<int> a1( /* args */ );
myvect v(a1)
mapInstance.insert(mymap::value_type(0, v));
тогда запись будет иметь копию v
данные, но не будет использовать свой распределитель, вместо этого он будет передан распределителю scoped_allocator_adaptor
, так будет построено так: myvect( v, A1<int>(mapInstance.get_allocator()) )
,
Если все это немного сбивает с толку, добро пожаловать в мой мир, но не волнуйтесь, в вашем случае vector<int>
будут всегда использование std::allocator<int>
,
Распределитель по умолчанию D
используется для push_back
вызов.
Фактически, распределитель по умолчанию используется для map
а потому что тип контейнера указывает использовать распределитель по умолчанию. Предполагая ваш A
наследуется от std::allocator
все, что происходит, это то, что ваш распределитель разделен на один по умолчанию, а контейнер ведет себя так же, как вы вообще не передавали экземпляр распределителя.