我有一个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?如果是,怎么样?
答案 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*/);
现在不再需要将指针传递给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
能够正确包含它需要执行的所有操作。
答案 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
当从地图中删除元素或销毁地图时,会自动清理内存。