Недавно я занимался разработкой собственного распределителя на основе пула памяти, который используется несколькими экземплярами распределителя.
Предполагалось, что распределитель будет совместим с контейнерами на основе STL и Standard C ++, такими как vector, deque, map, string и т. Д.
Однако что-то, в частности, вызвало у меня некоторое замешательство. Различные реализации контейнеров, такие как станд :: вектор, станд :: строка использовать Small Buffer Optimization — выделение на основе стека для небольших начальных требований к памяти.
Например, MSVC9.1 имеет следующий член в классе basic_string:
union _Bxty
{ // storage for small buffer or pointer to larger one
_Elem _Buf[_BUF_SIZE];
_Elem *_Ptr;
char _Alias[_BUF_SIZE]; // to permit aliasing
} _Bx;
Я не вижу, как при создании таких контейнеров можно уговорить
реализация только и всегда использовать предоставленный распределитель
и не использовать SBO. Я спрашиваю, потому что одно из намерений реализации
пользовательские распределители должны были использовать их в общей памяти
контекст, где объем разделяемой памяти может быть меньше
SBO ограничивают некоторые из различных реализаций, которые могут использовать.
Например, я хотел бы иметь ситуацию, когда я могу иметь два
экземпляры std :: string по одному на процесс, совместно использующий общий блок
памяти, которая может быть меньше или равна верхней части SBO
предел.
Возможно связано: Может ли std :: vector использовать небольшую буферную оптимизацию?
typedef std::vector<int,mysharedmemallocator> shmvtype;
shmvtype v(2,0); //<-- if SBO then error as memory is allocated on
//stack not via the allocator
v[1] = 1234; //<-- if SBO then error as wrong piece of memory
// is being modified.
Давайте посмотрим на другой пример, который не основан на разделяемой памяти, поскольку кажется, что он слишком усложняет некоторые вещи. Допустим, я хочу специализировать мой std :: basic_string или std :: vector и т. Д. С помощью распределителя, который заполняет выделяемую им память значением 0xAB до представления указателя обратно вызывающей сущности без какой-либо иной причины, кроме каприза.
Контейнеру, который специализируется на этом новом распределителе, но также использует SBO, не будет заполнена память на основе SBO шаблоном 0xAB. Так, например:
typedef std::basic_string<char,myfillmemallocator> stype
stype s;
s.resize(2);
assert(s[0] == 0xAB); // if SBO this will fail.
одно из намерений реализации пользовательских распределителей состояло в том, чтобы иметь возможность использовать их в контексте общей памяти
Это может быть то, что вы намереваетесь делать с этим, но это не то, почему они существуют. Действительно, за исключением basic_string
в C ++ 98/03 это не законно доля Распределение памяти между объектами вообще. Они могут совместно использовать объекты-распределители, чтобы они могли получать свою память из одного места. Но это незаконно для модификаций одного объекта, чтобы воздействовать на другой, который не связан; каждый экземпляр должен быть отдельным.
Строки копирования при записи работают только потому, что система предполагает, что любой неконстантный доступ к символу будет писать в него, таким образом выполняя копирование. И в C ++ 11 даже basic_string
запрещено делать такие вещи в стиле копирования при записи.
Например, я хотел бы иметь ситуацию, когда у меня может быть два экземпляра std :: string по одному на процесс, совместно использующий общий блок памяти, который может быть меньше или равен верхнему пределу SBO.
Это невозможно без написания собственного класса. Распределитель только контролирует, откуда берется память. То, что вам нужно, это гарантированная строка для копирования при записи или какой-то класс разделяемой строки.
То, что вы хотите, требует класса контейнера, специально разработанного для этой цели.
Других решений пока нет …