Как инициализировать универсальный базовый класс в c

#c #oop #c 11 #dynamic-memory-allocation

#c #ооп #c 11 #динамическое выделение памяти

Вопрос:

Я пытаюсь переписать некоторый старый код, чтобы сделать его более объектно-ориентированным. ранее у меня вызывалось объединение struct, скажем Event

 union Event {
  AEvent aEvent;
  BEvent bEvent;
  CEvent cEvent;
}
  

и это Event позже используется в структуре

 struct EventImpl {
  ... //some other members 
  Event event();
}
  

теперь я пытаюсь использовать мощный shared_ptr ;
Я создал новый базовый класс с именем BaseEvent

 class BaseEvent{
    size_t m_TypeHash; 
    public: 
        using Ptr = std::shared_ptr<BaseEvent>;
        virtual ~EventDesc() = default; 
        
        template <class T>
        // cast to different events
        static std::shared_ptr<T> Cast(const Ptramp; src) {
            return src->m_TypeHash == typeid(T).hash_code() 
                ? std::static_pointer_cast<T>(src)
                : nullptr;
        }
    protected: 
        BaseEvent(size_t typeHash) : m_TypeHash(typeHash){}
};
  

Отсюда, AEvent , BEvent и CEvent может просто расширяться BaseEvent , что делает код более чистым.

Но возникает вопрос, что мне делать со структурой EventImpl , которая содержит Event ?? Есть ли способ инициализировать универсальный BaseEvent класс?

 struct EventImpl {
  ... //some other members 
  BaseEvent<T> event; //??
  T event; // ??
  std::shared_ptr<BaseEvent> event; // ???
}

  

редактировать:
название EventImpl немного вводит в заблуждение, как указал Дмитрий, EventWrapper считает более подходящим. 🙂

Комментарии:

1. Не смешивайте шаблоны и наследование. Выберите один или другой.

2. Шаблоны хорошо переносят наследование при правильном использовании. Что меня смущает, так это «BaseEvent». Вы определяете его как класс, но затем используете его в EventImpl, как если бы это был шаблон

3. @Joe, ты прав, я намеревался использовать наследование классов. Я все еще пытаюсь освоить c . : p

4. Общий шаблон заключается в том, чтобы иметь EventImpl скрытую реализацию Event . Вы делаете обратное.

5. современное объединение — это std::variant (C 17)

Ответ №1:

Есть много разных способов сделать это. Прежде всего, вы можете использовать std::variant в качестве более ориентированной на ООП альтернативы union :

 using Event = std::variant<AEvent, BEvent, CEvent>;
  

Другой подход заключается в использовании динамического полиморфизма: вам нужно определить базовый класс для всех XEvent типов:

 class BaseEvent {};

class AEvent : public BaseEvent {};
  

Другой аспект динамического полиморфизма заключается в том, как возвращать значение: по необработанному указателю, по ссылке, интеллектуальному указателю и т. Д. Это std::shared_ptr может быть излишним, std::unique_ptr это должна быть первая идиома, которую следует учитывать.

 struct EventWrapper {
  std::unique_ptr<Event> event();
};
  

Я намеренно переименовал EventImpl to EventWrapper , потому что, если вы используете Impl идиому, следует использовать противоположное отношение:

 struct EventImpl;

class Event {
    std::unique_ptr<EventImpl> _impl;
};

class AEventImpl : public EventImpl {};
class BEventImpl : public EventImpl {};
class CEventImpl : public EventImpl {};
  

Комментарии:

1. да, спасибо, Дмитрий. Я понял, что именование EventImpl , вероятно, вводит в заблуждение. EventWrapper это то, что я имел в виду.

2. и не могли бы вы уточнить, почему это shared_ptr излишество? Я думал, что у меня есть несколько ptr для события ..?

3. shared_ptr Немного тяжелее, чем unique_ptr . И вы всегда можете преобразовать unique в shared, но не наоборот.