【发布时间】:2014-10-23 20:46:39
【问题描述】:
我在 C++ 应用程序中使用 GLFW 进行窗口和输入管理。为了使用成员函数作为关键事件的回调,我使用了答案 here 中建议的单例。
但是,我需要实现不同的输入处理程序。我的方法是使用 Singleton 作为实际输入处理程序 (controller_) 的包装器,以允许多态性。但是,对于要实例化的 Singleton,基类不能是抽象的。该解决方案涉及使用CRTP,以便能够从基类的实现中调用特定的输入处理方法。
template <class T>
class Controller : public BC{ //BC is just for using this class as a template parameter itself
public:
Controller(){}
Controller(Controller &controller){
controller_ = &controller;
}
static Controller& getInstance(Controller *controller){
static Controller instance(*controller);
return instance;
}
//This is the key move, where the concrete implementation is invoked.
static void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods){
Controller *aux;
(static_cast<T>(getInstance(aux))).keyCallbackImpl(window, key, scancode, action, mods);
}
//Stub to be overridden by the concrete input handler
virtual void keyCallbackImpl(GLFWwindow* window, int key, int scancode, int action, int mods){}
//This is the wrapped input handler
Controller *controller_;
};
这很好用。但是,派生类存在问题。为了能够执行强制转换,我必须定义一个转换构造函数。
SMController(Controller<SMController> &c){
controller_ = c.controller_;
std::cout << "Constructor" << std::endl;
}
这不方便有两个原因:
- 从 Controller 派生的用户必须明确定义此构造函数
- 每次按键时都构建一个新的包装器似乎很昂贵
使用这种设计是否可以替代这种转换?
编辑: 我最终接受了 T.C. 的提议,尽管略有不同。由于我需要具有自己的一组参数的子类,因此能够在构造函数中提供它们是理想的。初始化单例的单独调用很容易出错,因为它可能使用错误的模板参数来完成,或者只是被遗忘了。
为了能够在一次调用中使用其参数和对应的单例实例化专用对象,我继续使用 CRTP 并将这个构造函数添加到基类中:
Controller<T>(){
T::getInstance((T*)this);
}
现在,只需一个电话,我就能得到所需的一切:
std::shared_ptr<BaseController> c(new SMController(params_, window_));
【问题讨论】:
-
抱歉,如果 aux 未初始化,
getInstance(aux)应该如何工作?我错过了什么吗? -
单例是静态的,所以它只在第一次调用
getInstance()时被实例化(在其他地方完成)。 -
典型的 CRTP 实现将
this从Base<T> *转换为T *,或将*this从Base<T> &转换为T &。 -
很遗憾,我是在静态环境中投射的。
-
@broncoAbierto 同样的原则也适用。我是否正确理解
controller_应该存储指向派生的指针?
标签: c++ implicit-conversion crtp