Избегать использования «нового» в этой ситуации - PullRequest
2 голосов
/ 01 марта 2012

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

Но у меня проблема:

class ContentLoader
{
private:
    std::map<const char*, GFX::Material*> m_Materials;
    typedef std::pair<const char*, GFX::Material*> MTLPAIR;

public:
    ContentLoader();
    virtual ~ContentLoader();

    void RegisterMaterial(GFX::Material* mtl, const char* szName);
    GFX::Material* GetMaterial(const char* szName);
};

GFX :: Material является базовым классом, от которого наследуются несколько классов (материалов). Однако, чтобы передать один материал в RegisterMaterial (), необходимо использовать «new Specific_Material ()». Мы не можем просто скопировать объект GFX :: Material, поскольку он будет копировать только данные, содержащиеся в базовом классе.

Есть ли способ обойти это, поэтому мне не нужно использовать 'new' для передачи материала в загрузчик контента? Если да, то как?

Ответы [ 3 ]

2 голосов
/ 01 марта 2012

Я могу рассказать вам трюк, который больше подходит для того, чтобы обойти это.Похоже, вы создаете фабрику прототипов на основе шаблона прототипа: http://en.wikipedia.org/wiki/Prototype_pattern

Что вы можете сделать, это объявить статический экземпляр каждого из ваших специализированных классов и в конструкторе каждого специализированного класса вызвать RegisterMaterial.Например, для Specific_Material.cpp у вас должно быть:

SpecificMaterial SpecificMaterial::specificMaterialInstance;

, где specificMaterialInstance является статическим членом данных SpecificMaterialInstance.

По сути, все эти классы будут «самостоятельно регистрироваться».

Проблема этого метода заключается в том, что порядок создания статических экземпляров зависит от компилятора.Например, я помню, что некоторые компиляторы VC ++ просто делали это, основываясь на алфавитных именах модулей, поэтому экземпляр SpecificMaterialA был бы создан до SpecificMaterialB, поэтому у вас есть некоторая потеря контроля.

Я реализовал его для магистерской диссертацииПроект около 10 лет назад, так что я знаю, что это работает.

1 голос
/ 01 марта 2012

Рассматривайте использование подтипирования как подробность реализации.

Существует набор операций, которые вы, возможно, захотите выполнить на GFX::Material, поэтому класс GFX::Material должен реализовывать эти операции (Я не знаю, что это такое, но вот некоторые предположения):

//These can be automatically generated,
//but they are still part of the interface [
//Copy construct and assign
GFX::Material(GFX::Material const&);
GFX::Material& operator=(GFX::Material const&);
//Move construct and assign
GFX::Material(GFX::Material&&);
GFX::Material& operator=(GFX::Material&&);
//Destruct
GFX::~Material();
]
//Draw
void draw(Canvas& canvas, vec<2, float> posision);

//Stretch
void stretch(vec<2, float> scale);

//...

Теперь, чтобы настроить, как GFX :: Material реагирует на каждую из этих операций - дать ему параметризованный конструктор:

GFX::Material(/*Data to set up how I behave*/);

Теперь больше нет необходимости передавать указатели в ContentLoader, GFX::Material знает, как правильно копировать себя, перемещать себя и выполнять другие операции, которые зависят от его значения.


После этого ContentLoader можно переписать следующим образом:

class ContentLoader
{
private:
    std::map<std::string, GFX::Material> m_Materials;
    typedef std::pair<std::string, GFX::Material> MTLPAIR;

public:
    ContentLoader();
    ~ContentLoader();

    void RegisterMaterial(GFX::Material mtl, std::string szName);
    GFX::Material& GetMaterial(std::string const& szName);
};

Так как же на самом деле будет реализован GFX::Material

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

struct MaterialImplementaton {
    //Copy:
    virtual std::unique_ptr<MaterialImplementaton> clone() const = 0;
    //Destroy:
    virtual ~MaterialImplementaton() {}
    //Draw
    virtual void draw(Canvas& canvas, vec<2, float> posision) = 0;

    //Stretch
    virtual void stretch(vec<2, float> scale) = 0;

    //...
};

Теперь вы можете передать unique_ptr<MaterialImplementaton> в GFX::Material(/*Data to set up how I behave*/) для настройки поведения.GFX :: Material будет хранить clone_ptr<MaterialImplementation> и просто перенаправлять вызовы на MaterialImplementation.

. Это всего лишь одна из многих возможных реализаций GFX::Material.Важно то, что GFX::Material может правильно содержать все операции, которые ему необходимо выполнить.

0 голосов
/ 01 марта 2012

вам все равно потребуется new, однако, если карте принадлежит материал, вам следует использовать умные указатели, чтобы отразить это.

typedef std::unique_ptr<GFX::Material> mat_ptr_t;
typedef std::map<std::string, mat_ptr_t> mat_map_t;
mat_map_t materials;

materials["treecolouredstuff"]=mat_ptr_t(new WildernessMaterial());

UNTESTED

Когда элемент удаляется изкарта или карта уничтожена, память автоматически очищается.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...