Initialization class for other classes - C++

后端 未结 2 1045
被撕碎了的回忆
被撕碎了的回忆 2021-01-27 19:38

I would like to initialize 2 classes(say Class ARegister, Class BRegister) that registers some values(A,B). I want to initialize these two classes from a super(?) class( say Cla

2条回答
  •  鱼传尺愫
    2021-01-27 20:28

    It seems you have already decided on some kind of relationship between your objects. But, you only vaguely describe the relationship.

    If RegisterALL is using simple containment, then you would have a very simple relationship. This relationship might be expressed like this (please excuse the ASCII graphics):

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

    The advantage is that the picture for two dependents is very simple. However, if you are registering many objects, then the picture starts to look like RegisterALL is exploding into a bunch of XRegister objects.

    If RegisterALL is meant to contain ARegister and BRegister, you might want to create a common base class for ARegister and BRegister so that RegisterALL can maintain a container.

          +-------------+      +------------------+            <>--> := aggregates
          | RegisterALL |<>--->| AbstractRegister |              
          +-------------+      +------------------+              |
                                        |                      _/_\_ := inherits
                                       / \
                                   ___/___\___
                                   |         |
                        +-----------+       +-----------+
                        | ARegister |       | BRegister |
                        +-----------+       +-----------+
    

    We see that no matter how many new items get registered, the relationship between RegisterALL and AbstractRegister remains the same. We can go a step further, and derive ARegister and BRegister from a template.

          +-------------+      +------------------+
          | RegisterALL |<>--->| AbstractRegister |
          +-------------+      +------------------+
                                        |
                                       / \
                                      /___\
                                        |
                                        |     +--------------+
                               +--------------| RegisterType |
                               |              +--------------+
                               | RegisterTemplate |
                               +------------------+
    

    Okay, so much for the the OO design lesson. This translates to code pretty quickly. Let's start with the easy things. RegisterType enumerates the different things to register. RegisterTypeName and the overloaded operator allow the code to print a string instead of a number when printing a 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 provides an interface to query for this type. In addition, a poke interface is provided with a default implementation. Note in C++, abstract types should provide a virtual destructor.

    class AbstractRegister {
    public:
        virtual ~AbstractRegister () {}
        virtual RegisterType type () const = 0;
        virtual void poke () { std::cout << "Poked " << type(); }
    };
    
    typedef std::unique_ptr AbstractRegisterPtr;
    static const AbstractRegisterPtr AbstractRegisterNullPtr;
    

    The RegisterALL class has a container to hold things of type AbstractRegister. It uses a map to associate RegisterType to the AbstractRegister instance, which we are taking to be the registration. RegisterALL is implemented as a singleton, meaning it only allows one instance of itself. The add method performs the registration, and the find method allows a registered instance to be found. The implementation of the RegisterALL constructor is deferred until after the definition of a RegisterTemplate.

    class RegisterALL {
        template  friend class RegisterTemplate;
        typedef std::unique_ptr SelfPtr;
        typedef std::map 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;
    

    The RegisterTemplate class derives from the AbstractRegister and is parameterized by a RegisterType. It implements the type virtual method by returning the value of its template parameter. It also employs singleton, but it does not make its instance public. Instead, its instance is managed by RegisterALL. It provides the register_type method that registers itself with RegisterALL, and this instance can only be found by using the find method on RegisterALL.

    template 
    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);
        }
    };
    

    The RegisterALL constructor employs the helper template register_all that iterates through the RegisterType enum, and instantiates the corresponding RegisterTemplate, thereby causing all RegisterType's to be registered with RegisterALL.

    template 
    struct register_all {
        register_all () {
            RegisterTemplate(X)>::register_type();
            register_all();
        }
    };
    
    template <> struct register_all {};
    
    inline RegisterALL::RegisterALL ()
    {
        one = std::move(SelfPtr(this));
        register_all<0>();
    }
    

    So to try it out, we use the following code:

    RegisterALL::instance();                  // registers all RegisterType's
    RegisterTemplate::register_type();     // try to register B again
    RegisterALL::instance()->find(A)->poke(); // poke at A
    

    And this is the output:

    Created A
    Created B
    Already registered B
    Poked A
    Destroying B
    Destroying A
    

    Notice how the smart pointers automatically clean up the registered items for us.

提交回复
热议问题