После того, как я несколько дней буду изучать allocator
, читая несколько статей
(cppreference и у нас не хватает памяти),
Я запутался в том, как управлять структурой данных, чтобы распределять память определенным образом.
Я совершенно уверен, что что-то неправильно понял,
поэтому я разделю остальную часть вопроса на множество частей, чтобы легче было отреагировать на мою ошибку.
Вот что я (неправильно) понимаю:
отрывок
Предположим, что B::generateCs()
является функцией, которая генерирует список C
из списка CPrototype
. B::generateCs()
используется в конструкторе B()
: -
class C {/*some trivial code*/};
class CPrototype {/*some trivial code*/};
class B {
public:
std::vector<C> generateCs() {
std::vector<CPrototype> prototypes = getPrototypes();
std::vector<C> result; //#X
for(std::size_t n=0; n < prototypes.size(); n++) {
//construct real object (CPrototype->C)
result.push_back( makeItBorn(prototypes[n]) );
}
return result;
}
std::vector<C> bField; //#Y
B() {
this->bField = generateCs(); //#Y ; "generateCs()" is called only here
}
//.... other function, e.g. "makeItBorn()" and "getPrototypes()"
};
Из приведенного выше кода std::vector<C>
настоящее время использует стандартный std::allocator
умолчанию.
Для простоты, скажем, теперь есть только 2 распределителя (кроме std::allocator
),
который я могу кодировать сам или изменить откуда- то:
- HeapAllocator
- StackAllocator
Часть 1 (#X)
Этот фрагмент может быть улучшен с помощью специального распределителя типов.
Это может быть улучшено в 2 местах. (#X
и #Y
)
std::vector<C>
в строке #X
представляется переменной стека,
поэтому я должен использовать stack allocator
: -
std::vector<C,StackAllocator> result; //#X
Это приводит к увеличению производительности. (#X
закончен.)
Часть 2 (#Y)
Далее, более сложная часть находится в конструкторе B()
. (#Y
)
Было бы хорошо, если бы у переменной bField
был соответствующий протокол распределения.
Простое кодирование вызывающей стороны для использования распределителя явно не может этого достичь, потому что вызывающая сторона конструктора может делать только лучше, чем: -
std::allocator<B> bAllo;
B* b = bAllo.allocate(1);
который не имеет никакого влияния на протокол распределения bField
.
Таким образом, обязанностью самого конструктора является выбор правильного протокола распределения.
Часть 3
Я не могу знать, будет ли экземпляр B
создан как переменная кучи или переменная стека.
Это важно, потому что эта информация важна для выбора правильного распределителя/протокола.
Если я знаю, какой это (куча или стек), я могу изменить объявление bField
:
std::vector<C,StackAllocator> bField; //.... or ....
std::vector<C,HeapAllocator> bField;
К сожалению, с ограниченной информацией (я не знаю, какая это будет куча/стек, это может быть и то и другое),
этот путь (используя std::vector
) ведет в тупик.
Часть 4
Следовательно, лучше всего передать в конструктор распределитель:
MyVector<C> bField; //create my own "MyVector" that act almost like "std::vector"
B(Allocator* allo) {
this->bField.setAllocationProtocol(allo); //<-- run-time flexibility
this->bField = generateCs();
}
Это утомительно, потому что вызывающие абоненты должны передавать распределитель в качестве дополнительного параметра,
но других путей нет.
Более того, это единственный практический способ получить указанное ниже преимущество согласованности данных, когда есть много вызывающих абонентов, каждый из которых использует свой собственный блок памяти: -
class System1 {
Allocator* heapForSystem1;
void test(){
B b=B(heapForSystem1);
}
};
class System2 {
Allocator* heapForSystem2;
void test(){
B b=B(heapForSystem2);
}
};
Вопрос
- Где я начал идти не так, как?
- Как я могу улучшить фрагмент, чтобы использовать соответствующий распределитель (
#X
и#Y
)? - Когда я должен передать allocator в качестве параметра?
Трудно найти практический пример использования распределителя.
Редактировать (ответить Уолтер)
... использование другого, чем std: allocator <>, редко рекомендуется.
Для меня это ядро ответа Уолтера.
Это было бы ценным знанием, если оно надежно.
1. Существуют ли какие-либо книги/ссылки/ссылки/доказательства, которые поддерживают это?
Список не поддерживает претензию. (Это на самом деле немного поддерживает противоположность.)
Это из личного опыта?
2. Ответ как-то противоречит многим источникам. Пожалуйста, защита.
Есть много источников, которые рекомендуют не использовать std:allocator<>
.
- У нас не хватает памяти:
Не могу ответить "Сколько памяти вы используете для подсистемы X?" виновен - Пользовательские C++ распределители, подходящие для видеоигр
Это означает, что пользовательский распределитель является обязательным для консольных игр.
@раздел "Зачем заменять распределитель по умолчанию?" - Управление памятью часть 1 из 3
без специального распределителя = "время от времени есть небольшое отставание (в игре)"
Точнее говоря, это просто "ажиотаж", который редко стоит использовать в реальном мире?
Еще один маленький вопрос: -
Может ли утверждение быть расширено до "В большинстве качественных игр редко используется собственный распределитель"?
3. Если я нахожусь в такой редкой ситуации, я должен оплатить стоимость, верно?
Есть только 2 хороших способа:
- передача распределителя в качестве аргумента шаблона, или
- как параметр функции (включая конструктор)
- (еще один плохой подход - создать глобальный флаг о том, какой протокол использовать)
Это правильно?