Назначить распределитель для поля - я должен создать распределитель по умолчанию в качестве глобальной переменной? - PullRequest
0 голосов
/ 07 июня 2018

Моя игра Движок имеет 2 распределителя: куча и стек.

class GameEngine{public:
    Allocator* heap;  //<-- a custom allocator class
    Allocator* stack;
}

Сегодня я хочу создать класс Car , которому требуется распределитель кучи.

class Car{
    MyArray<Car*> nearEnemyCache; //<-- my custom storage, need allocator
};

Таким образом, чтобы создать экземпляр автомобиля, я вырываю распределитель кучи из engine и назначаю его на car .

class CarFactory{
    GameEngine* gameEngine;
    Car* createCar(){
        Car* car=new Car();
        car->nearEnemyCache.setAllocator( gameEngine->heap );
        return car;
    }
};

Вот диаграмма: -

enter image description here

Задача

Если класс car растет и имеет много полей, которые требуютallocator: -

class Car{
    MyArray<Car*> nearEnemyCache; //<-- need allocator
    class A{    };
    MyArray<A> as;  //<--- need allocator
    class B{}; 
    MyUnorderMap<B> bs; //<--- need allocator
};

Мне придется присваивать им распределитель, один за другим вручную.
Это приводит к грязному коду.

Car* createCar(){
    Car* car=new Car();
    car->nearEnemyCache.setAllocator( gameEngine->heap );
    car->as.setAllocator( gameEngine->heap );
    car->bs.setAllocator( gameEngine->heap );   //<-- so dirty & tedious
    return car;
}

My Poor Solutions

Оба решения используют глобальную переменную в качестве распределителя по умолчанию , прямо или косвенно.
Я считаю, что использование глобальной переменной, как правило, плохопрактика.

1.глобальный распределитель по умолчанию

Установите глобальный распределитель в начале программы и используйте его по умолчанию, например: -

Allocator* defaultAllocator;
template<class T>MyArray{
    Allocator* allocator=defaultAllocator;
    //... other stuffs
}
int main(){
    //create "gameEngine"
    defaultAllocator=gameEngine->heap;
    // start game
}

2.глобальная переменная игрового движка.

GameEngine* defaultGameEngine;
template<class T> class MyArray{
    Allocator* allocator=defaultGameEngine->heap;
    //... other stuffs
}
int main(){
    //create "gameEngine"
    defaultGameEngine=gameEngine;
    // start game
}

Подобные вопросы: -

1 Ответ

0 голосов
/ 07 июня 2018

Если вы не можете представить себе случай, когда существует более одного стека / кучи / движка, то это, вероятно, лучше всего выразить как синглтон.

Это также может быть связано с шаблонами ваших контейнеров: Если вы знаю ваша машина будет когда-либо использовать только выделение кучи, тогда наличие такого члена, как MyArray<Car*, HeapAllocator> nearEnemyCache;, позволит контейнеру просто получить доступ к соответствующему синглтону (и скрыт эту часть из вашей «реальной» логики, поэтому больше не будет грязного)там).

template<class T, class DefaultAlloc = ChosenAtRuntimeAlloc>
class MyArray
{
public:
    MyArray() : _allocator(singleton<DefaultAlloc>::get())
    {
    }

    void setAllocator(Allocator* alloc)
    {
        _allocator = alloc;
    }

private:
    Allocator* _allocator;
};

Затем вы можете установить его во время выполнения, если хотите, но не обязательно делать это, если вы знаете это во время компиляции.Класс ChosenAtRuntimeAlloc просто выдаст исключение, когда его попросят выделить ("Must set an allocator at runtime before allocating!").

Если вы всегда знаете, какие распределители будут использовать ваши контейнеры (то есть вам никогда не придетсяпо умолчанию второй параметр шаблона), тогда вы можете даже исключить все виртуальные функции из ваших распределителей и вместо этого позволить компилятору встроить все эти функции.Это в основном способ, которым стандартные контейнеры библиотеки делают это.

...