我有一个类模板ResourceManager
,它的用途是这样的:
ResourceManager<Image>* rm =
ResourceManager<Image>::Instance();
Image* img = rm->acquire("picture.jpg");
rm->release(img);
我想使用依赖注入(将ResourceManager
作为参数传递给应该使用它而不是全局使用的函数),但是鉴于它是一个模板,我不知道如何去做这个。你有什么建议吗?
我的游戏仅在开发的开始阶段,我已经有四种资源类型(Image
,Font
,Animation
和Sound
)所以制作一个{具有每种类型资源的获取功能的{1}}(即不是模板)不是一种选择。
编辑:一些澄清。
我正在寻找的不是如何为一个类型的ResourceManager进行依赖注入,而是一次性为所有这些进行依赖注入。
我的ResourceManager
个对象在初始化/打开时需要加载资源;他们通过GameState
这样做。但是,ResourceManagers
可能需要加载任意数量的资源类型:动画,字体,图像,声音等 - 这是每种GameStates
的许多函数参数!你有什么建议我做的?
答案 0 :(得分:1)
好吧,如果函数需要特定类型的资源(可能是大多数资源),只需将特定模板实例定义为参数:
function(ResourceManager<Image> *rm, ...);
如果该功能需要任何类型的资源
成为模板本身,例如:
template <typename T>
function(ResourceManager<T> *rm, ...);
它可能需要引用从资源管理器获取的资源,因此无论如何它都需要更多地方的模板参数。
使用多态基类。这意味着你必须定义像
这样的东西class ResourceManagerBase { /* methods you need to call via the base class */ };
template <typename T>
class ResourceManager : ResourceManagerBase { ... };
function(ResourceManagerBase *rm, ...)
该函数可以调用基类中定义的任何方法。如果方法在内部依赖于资源类型,则它们将在基础中声明为抽象虚拟(virtual returnType method(...) = 0
)并在模板类本身中定义。您还可以使用dynamic_cast
来检查您所拥有的ResourceManager
的具体实例。
注意,如果函数需要引用资源,那么您同样需要所有资源的ResourceBase
抽象基类,因此您可以引用任何类型的资源。
选择是在具有模板功能的较快但非常大的代码之间进行权衡(该功能将针对每个专业化单独编译)或者使用虚拟方法较慢但较小的代码(调用虚拟方法较慢,但有没有代码重复)。此外,模板变体编译速度较慢,因为大多数编译器将为使用它的每个目标文件生成代码,而不是在链接时合并相同的副本。
答案 1 :(得分:0)
使用构造函数注入的示例:
template<typename T>
struct ResourceManager
{
virtual T* acquire(std::string const& resourceName)
{ return ...; }
... etc ...
};
class ImageUser
{
ResourceManager<Image>* rm_;
public:
explicit ImageUser(ResourceManager<Image>* rm)
: rm_(rm)
{}
ImageUser()
: rm_(ResourceManager<Image>::Instance())
{}
void UseImage()
{
Image* img = rm_->acquire("picture.jpg");
rm_->release(img);
}
};
struct ImageResourceManagerFake : ResourceManager<Image>
{
virtual Image* acquire(std::string const& resourceName) // override
{ return <whatever-you-want>; }
... etc ...
};
void test_imageuser()
{
ImageResourceManagerFake* rm = new ImageResourceManagerFake;
ImageUser iu(rm);
... test away ...
}
请注意,我遗漏了所有资源管理;在适用的地方使用智能指针等。
答案 2 :(得分:0)
首先。请记住,在使用模板时,必须在编译时解决所有问题。
然后代码中的ResourceManager似乎是单例。因此,粗略地讲,与全局变量没有区别。
当你可以直接调用单身人士时,我认为这是无用的传递rm作为你的函数的参数。
我希望,这可以解决你的问题。
答案 3 :(得分:0)
即使在你的编辑之后,我发现有点难以理解你所看到的是什么而没有看到整个画面,但我会再次尝试一个基本的例子:
template<typename T>
struct ResourceManager
{
virtual T* acquire(std::string const& resourceName)
{ return ...; }
// ... etc ...
};
class ImageUser
{
ResourceManager<Image>* rm_;
public:
explicit ImageUser(ResourceManager<Image>* rm)
: rm_(rm)
{}
void UseImage()
{
Image* img = rm_->acquire("picture.jpg");
rm_->release(img);
}
};
template<typename T>
struct ResourceManagerFake : ResourceManager<T>
{
T* acquireRetVal;
virtual T* acquire(std::string const& resourceName)
{ return acquireRetVal; }
// ... etc ...
};
void test_imageuser()
{
ResourceManagerFake<Image>* rm = new ResourceManagerFake<Image>;
rm->acquireRetVal = new Image;
ImageUser iu(rm);
iu.UseImage();
}
评论者注意:我很清楚资源泄漏,但这不是重点。