0

我想初始化注册一些值(A,B)的2个类(比如ARegister类,BRegister类)。我想从一个超级(?)类(比如类RegisterALL)初始化这两个类。

例如:RegisterALL 类将初始化 ARegister 和 BRegister。所以使用该模块的任何人都不需要单独创建 ARegister 和 BRegister 对象,而是可以创建 RegisterALL 对象。

我想在构造函数中完成所有这些注册。因此,需要做的就是创建 RegisterALL 类的对象,并且注册会自动发生。

我们在项目中不使用异常。因此,如果注册中有错误,则无法知道。

我应该有单独的成员函数来进行注册还是让构造函数进行注册。

我是 OO 设计的新手。我觉得设计有问题,如果您能指出正确的方法,将会很有帮助。

4

2 回答 2

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 问题,但我不想在这里讨论。如果您想要一个更简单的方法,我建议您阅读有关工厂设计模式的内容。

于 2012-06-06T01:19:40.607 回答
3

看来您已经决定了对象之间的某种关系。但是,你只是模糊地描述了这种关系。

如果RegisterALL是使用简单的遏制,那么你将有一个非常简单的关系。这种关系可以这样表达(请原谅 ASCII 图形):

       +-------------+
       | RegisterALL |               --> := has
       +-------------+
          |       |
          v       v
+-----------+   +-----------+
| ARegister |   | BRegister |
+-----------+   +-----------+

优点是两个家属的照片非常简单。但是,如果您要注册许多对象,则图片开始看起来像是RegisterALL爆炸成一堆XRegister对象。

如果RegisterALL要包含ARegisterand BRegister,您可能希望为 and 创建一个公共基类ARegisterBRegister以便RegisterALL可以维护容器。

      +-------------+      +------------------+            <>--> := 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。它使用映射关联RegisterTypeAbstractRegister我们将其作为注册的实例。RegisterALL被实现为单例,这意味着它只允许它自己的一个实例。该add方法执行注册,并且该find方法允许找到已注册的实例。构造函数的实现RegisterALL被推迟到 a 的定义之后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并由 a 参数化RegisterType。它type通过返回其模板参数的值来实现虚方法。它也使用单例,但不公开其实例。相反,它的实例由RegisterALL. 它提供了向register_type注册自己的方法RegisterALL,而这个实例只能通过findon 的方法找到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_allRegisterTypeRegisterTemplateRegisterTypeRegisterALL

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

请注意智能指针如何为我们自动清理已注册的项目。

于 2012-06-06T02:42:04.250 回答