在这种情况下避免使用“新”

时间:2012-03-01 15:59:55

标签: c++ memory-management

我有一个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'将材料传递给contentloader?如果是,怎么样?

3 个答案:

答案 0 :(得分:2)

我可以告诉你一个伎俩,更多的是黑客来解决这个问题。您似乎正在构建基于原型模式的原型工厂:http://en.wikipedia.org/wiki/Prototype_pattern

您可以做的是,声明每个专用类的静态实例,并在每个专用类的构造函数中调用RegisterMaterial。例如,对于Specific_Material.cpp,您将拥有:

SpecificMaterial SpecificMaterial::specificMaterialInstance;

其中specificMaterialInstance是SpecificMaterialInstance的静态数据成员。

从本质上讲,所有这些课程都将“自我注册”。

此方法的问题在于,创建静态实例的顺序取决于编译器。例如,我记得一些VC ++编译器会根据模块的字母名称来执行它,因此SpecificMaterialA实例将在SpecificMaterialB之前构建,因此您将失去一些控制权。

我已经在大约10年前完成了硕士论文项目,所以我知道它有效。

答案 1 :(得分:1)

将子类型的使用视为实现细节。

您可能希望对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*/);

现在不再需要将指针传递给ContentLoaderGFX::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能够正确包含它需要执行的所有操作。

答案 2 :(得分:0)

你仍然需要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

当从地图中删除元素或销毁地图时,会自动清理内存。