Вопрос по c++ – Класс инициализации для других классов - C ++

0

Я хотел бы инициализировать 2 класса (скажем, Class ARegister, Class BRegister), который регистрирует некоторые значения (A, B). Я хочу инициализировать эти два класса из супер (?) Класса (скажем, Class RegisterALL).

Пример: класс RegisterALL будет инициализировать ARegister и BRegister. Таким образом, любому, кто использует модуль, не нужно создавать объекты ARegister и BRegister по отдельности, вместо этого они могут создавать объекты RegisterALL.

Я хотел бы сделать все эти регистрации в конструкторе. Поэтому все, что нужно сделать, - это создать объекты класса RegisterALL, и регистрация происходит автоматически.

Мы не используем исключения в нашем проекте. Так что, если в регистрации есть какая-то ошибка, это невозможно узнать.

Должен ли я иметь отдельную функцию-член, которая будет выполнять регистрацию, или позволить конструктору делать регистрацию.

Я новичок в дизайне ОО. Я чувствую, что с дизайном что-то не так, будет полезно, если вы укажете правильный подход.

Когда я был новичком в ОО, я пытался написать все в ОО-стиле, что было слишком сложно. Со временем я научился писать наиболее важные части в ОО-стиле, оставляя менее важный код в старом стиле. Я приобрел опыт таким образом и постепенно начал писать все больше и больше ОО. Я пытаюсь сказать следующее: вы, возможно, не захотите OO-ify весь ваш код за раз, но начнете с OO-ify, если он будет управляемым. thb
Да, мы используем «новый». Мы проверяем NULL после инициализации объекта с помощью «new». Saaras
Не могли бы вы вернуться и принять некоторые ответы? Это побудит людей помочь вам. Hassan
Если у вас не может быть исключений, то обычный способ состоит в том, чтобы функция init () возвращала bool. Но будьте осторожны с копией ctor Martin Beckett
& quot; Мы не используем исключения в нашем проекте. & quot; Есть ли для этого веская причина? Вы отключаете все исключения (например, еслиnew терпит неудачу, это бросает? ВернутьNULL? Вы не используетеnew?) James McNellis

Ваш Ответ

2   ответа
3

вы уже определились с какими-то отношениями между вашими объектами. Но вы только смутно описываете отношения.

ЕслиRegisterALL использует простое сдерживание, тогда у вас будут очень простые отношения. Это отношение может быть выражено так (пожалуйста, извините за графику ASCII):

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

Преимущество в том, что картина для двух иждивенцев очень простая. Однако, если вы регистрируете много объектов, картинка начинает выглядетьRegisterALL взрывается в кучуXRegister объекты.

ЕслиRegisterALL предназначен для содержанияARegister а такжеBRegisterвы можете создать общий базовый класс дляARegister а такжеBRegister чтобыRegisterALL может поддерживать контейнер.

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

Мы видим, что независимо от того, сколько новых предметов будет зарегистрировано, отношения междуRegisterALL а такжеAbstractRegister остается такой же. Мы можем пойти еще дальше и вывестиARegister а такжеBRegister из шаблона.

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

Хорошо, так много для урока дизайна ОО. Это переводит в код довольно быстро. Давайте начнем с простых вещей.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 enum и создает соответствующие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

Обратите внимание, как умные указатели автоматически очищают для нас зарегистрированные товары.

@Saaras: не переживай. Вы выбираете то, что чувствуете, дает вам лучший ответ на ваш вопрос.
Много хороших вещей, чтобы учиться из кода. Я не уверен, как принять 2 ответа. Saaras
@Saaras: я закончил с этим примером. Дайте мне знать, если у вас есть вопросы.
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 состоит в том, что производные классы не должны знать, как зарегистрироваться в базовом классе. список, это сделано для них. Что касается обработки ошибок, то она также может быть реализована в каждом производном классе ». функции создателя. Есть лучшие способы справиться с проблемой идентификатора, но я не хочу вдаваться в подробности здесь. Если вы хотите более простой метод, я предлагаю прочитать о шаблоне проектирования фабрики.

+1 Может быть слишком сложно для новичка, но тем не менее это хороший трюк для изучения.
Новички становятся старыми, изучая сложные трюки :)
Спасибо, это выглядит великолепно. Saaras

Похожие вопросы