我想初始化两个注册一些值(A,B)的类(比如Class ARegister,Class BRegister)。我想从超级(?)类(比如Class RegisterALL)初始化这两个类。
Ex:RegisterALL类将初始化ARegister和BRegister。因此,任何使用该模块的人都不需要单独创建ARegister和BRegister的对象,而是可以创建RegisterALL的对象。
我想在构造函数中执行所有这些注册。因此,所有需要做的就是创建RegisterALL类的对象,并自动进行注册。
我们不在项目中使用例外。因此,如果注册中存在一些错误,则无法知道。
我是否应该有单独的成员函数来进行注册或让构造函数进行注册。
我是OO设计的新手。我觉得设计有问题,如果你能指出正确的方法,将会有所帮助。
答案 0 :(得分:3)
最好的方法是使用CRTP(奇怪的重复模板模式),派生类ARegister和BRegister将自己作为模板参数传递给基类RegisterALL。 它看起来像这样:
class RegisterAll
{
public:
template<class DerivedType>
DerivedType *createType()
{
RegisterAll *r = (*(m_creators[DerivedType::id]))();
return dynamic_cast<DerivedType *>(r); //static_cast will work too if you didn't make a mistake
}
protected:
static std::map<int,RegisterAll *(*)()> m_creators;
};
std::map<int,RegisterAll *(*)()> RegisterAll::m_creators = std::map<int,RegisterAll *(*)()>();
template<class Derived>
class CRTPRegisterAll : public RegisterAll
{
public:
static bool register()
{
RegisterAll::m_creators.push_back(std::make_pair(Derived::id,Derived::create);
return true;
}
private:
static bool m_temp;
};
template<class Derived>
bool CRTPRegisterAll<Derived>::m_temp = CRTPRegisterAll<Derived>::register();
class RegisterA : public CRTPRegisterAll<RegisterA>
{
private:
static RegisterA *create()
{
//do all initialization stuff here
return new RegisterA;
}
public:
static const int id = 0;
};
现在,m_temp
中静态变量CRTPRegisterAll
的初始化将每个派生类型的创建者函数推送到RegisterAll
列表中。让RegisterAll
知道所有派生类(它不是非常可扩展的)通常不是很好的设计。这样,实际的创建方法可以在每个派生类中实现,创建者函数将自动在RegisterAll
中注册。 CRTP的一个主要优点是派生类不需要知道如何将自己注册到基类列表,它是为它们完成的。至于错误处理,也可以在每个派生类的创建者函数中实现。有更好的方法来处理id问题,但我不想在这里进入。
如果你想要一个更简单的方法,我建议阅读有关工厂设计模式的内容。
答案 1 :(得分:3)
您似乎已经决定了对象之间的某种关系。但是,你只是模糊地描述了这种关系。
如果RegisterALL
使用的是简单的遏制,那么您将拥有一个非常简单的关系。这种关系可能表达如下(请原谅ASCII图形):
+-------------+
| RegisterALL | --> := has
+-------------+
| |
v v
+-----------+ +-----------+
| ARegister | | BRegister |
+-----------+ +-----------+
优点是两个家属的图片非常简单。但是,如果您要注册许多对象,那么图片看起来就像是RegisterALL
爆炸成一堆XRegister
个对象。
如果RegisterALL
包含ARegister
和BRegister
,您可能需要为ARegister
和BRegister
创建公共基类,以便RegisterALL
1}}可以维护一个容器。
+-------------+ +------------------+ <>--> := aggregates
| RegisterALL |<>--->| AbstractRegister |
+-------------+ +------------------+ |
| _/_\_ := inherits
/ \
___/___\___
| |
+-----------+ +-----------+
| ARegister | | BRegister |
+-----------+ +-----------+
我们发现,无论注册了多少新项目,RegisterALL
和AbstractRegister
之间的关系都保持不变。我们可以更进一步,从模板中派生ARegister
和BRegister
。
+-------------+ +------------------+
| RegisterALL |<>--->| AbstractRegister |
+-------------+ +------------------+
|
/ \
/___\
|
| +--------------+
+--------------| RegisterType |
| +--------------+
| RegisterTemplate |
+------------------+
好的,OO设计课程非常多。这很快转化为代码。让我们从简单的事情开始吧。 RegisterType
枚举要注册的不同内容。 RegisterTypeName
和重载运算符允许代码在打印RegisterType
时打印字符串而不是数字。
enum RegisterType { A, B, MAX_RegisterType };
static inline std::string
RegisterTypeName (RegisterType t)
{
static const char * names[] = { "A", "B" };
return names[t];
}
static inline std::ostream &
operator << (std::ostream &output, RegisterType t)
{
return output << RegisterTypeName(t);
}
AbstractRegister
提供了一个查询此类型的接口。此外,poke
接口提供了默认实现。注意在C ++中,抽象类型应该提供虚拟析构函数。
class AbstractRegister {
public:
virtual ~AbstractRegister () {}
virtual RegisterType type () const = 0;
virtual void poke () { std::cout << "Poked " << type(); }
};
typedef std::unique_ptr<AbstractRegister> AbstractRegisterPtr;
static const AbstractRegisterPtr AbstractRegisterNullPtr;
RegisterALL
类有一个容器来容纳AbstractRegister
类型的东西。它使用地图将RegisterType
与AbstractRegister
实例相关联,我们将其作为注册。 RegisterALL
实现为单例,这意味着它只允许自身的一个实例。 add
方法执行注册,find
方法允许找到已注册的实例。 RegisterALL
构造函数的实现推迟到定义RegisterTemplate
之后。
class RegisterALL {
template <RegisterType> friend class RegisterTemplate;
typedef std::unique_ptr<RegisterALL> SelfPtr;
typedef std::map<RegisterType, AbstractRegisterPtr> RegisterMap;
void add (AbstractRegister *r) { all[r->type()] = AbstractRegisterPtr(r); }
RegisterALL ();
public:
static const SelfPtr & instance () {
if (!one) new RegisterALL;
return one;
}
const AbstractRegisterPtr & find (RegisterType t) const {
RegisterMap::const_iterator i = all.find(t);
return (i != all.end()) ? i->second : AbstractRegisterNullPtr;
}
private:
static SelfPtr one;
RegisterMap all;
};
RegisterALL::SelfPtr RegisterALL::one;
RegisterTemplate
类派生自AbstractRegister
,并由RegisterType
参数化。它通过返回其模板参数的值来实现type
虚方法。它也使用单例,但它不公开其实例。相反,它的实例由RegisterALL
管理。它提供了使用register_type
注册自己的RegisterALL
方法,只有使用find
上的RegisterALL
方法才能找到此实例。
template <RegisterType RT>
class RegisterTemplate : public AbstractRegister {
RegisterType type () const { return RT; }
void poke () {
std::cout << "Poked " << RegisterTypeName(RT) << std::endl;
}
RegisterTemplate () {
std::cout << "Created " << RegisterTypeName(RT) << std::endl;
}
~RegisterTemplate () {
std::cout << "Destroying " << RegisterTypeName(RT) << std::endl;
}
public:
static void register_type () {
if (RegisterALL::instance()->find(RT)) {
std::cout << "Already registered " << RegisterTypeName(RT)
<< std::endl;
return;
}
RegisterALL::instance()->add(new RegisterTemplate<RT>);
}
};
RegisterALL
构造函数使用辅助模板register_all
迭代RegisterType
枚举,并实例化相应的RegisterTemplate
,从而导致所有RegisterType
在RegisterALL
注册。
template <unsigned X>
struct register_all {
register_all () {
RegisterTemplate<static_cast<RegisterType>(X)>::register_type();
register_all<X+1>();
}
};
template <> struct register_all<MAX_RegisterType> {};
inline RegisterALL::RegisterALL ()
{
one = std::move(SelfPtr(this));
register_all<0>();
}
所以要试一试,我们使用以下代码:
RegisterALL::instance(); // registers all RegisterType's
RegisterTemplate<B>::register_type(); // try to register B again
RegisterALL::instance()->find(A)->poke(); // poke at A
这是输出:
Created A
Created B
Already registered B
Poked A
Destroying B
Destroying A
注意智能指针如何自动清理我们注册的项目。