Как инициализировать дочерний класс из указателя на его абстрактный материнский класс?

Итак, я новичок в С++, и моя проблема заключается в следующем: у меня есть абстрактный класс, который является несколькими дочерними классами (и у них были и другие дочерние классы). Я пытаюсь использовать полиморфизм для сериализации и десериализации этих дочерних классов и иметь в своем абстрактном классе:

    virtual QDataStream& serialize(QDataStream& stream)=0;
    virtual QDataStream& deserialize(QDataStream &stream)=0;

Итак, мой вопрос прост: когда я десериализую, я хочу вызвать функцию десериализации, и я еще не знаю, какой дочерний класс я получаю.

Функция выглядит следующим образом:

QDataStream& operator>>(QDataStream& in, SomeClass& i){

    std::shared_ptr<AbstractClass> ptr;
    ptr->deserialize(in); //Raises an SIGSEGV error

}

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

Спасибо!

Изменить: мой первый подход, который работает, заключался в том, чтобы также передать QDataStream имя моего дочернего класса, чтобы я знал, какой класс создавать:

        QString myClassName;
        in >> myClassName;
        if(myClassName == "ChildClass1"){
            std::unique_ptr<AbstractClass> ptr(new ChildClass1);
            ptr->deserialize(in);
        }

Но я чувствую, что это действительно не чистый способ ООП!


person trablazar    schedule 10.06.2019    source источник


Ответы (2)


К сожалению, невозможно инициализировать дочерний класс только из базового класса. При сериализации вам придется сериализовать какой-то идентификатор для дочернего класса. Далее при десериализации я предлагаю вам использовать шаблон factory. Используя фабрику, вы передаете данные идентификатора и класса, которые хотите десериализовать. Эта фабричная функция создаст требуемый дочерний класс и вернет указатель на базовый класс.

person Bart    schedule 10.06.2019
comment
Привет, @Барт, понятно! Таким образом, фабричный шаблон был бы самым элегантным решением? Спасибо за предложение, я прочитаю вашу ссылку :) - person trablazar; 10.06.2019
comment
Это то, что я использую в этой ситуации. - person Bart; 10.06.2019

 std::shared_ptr<AbstractClass> ptr;

Инициализированный общий указатель по умолчанию указывает на null.

Поведение косвенного обращения через нулевой указатель не определено.

ptr->deserialize(in); //Raises an SIGSEGV error

Это косвенно через нулевой указатель. Поведение программы не определено.


Вы можете упростить свой рабочий пример, избавившись от динамического распределения:

ChildClass1 c;
c.deserialize(in);

В этом случае вам не нужен полиморфизм.


то, что я пытаюсь сделать, это создать экземпляр и вызвать любой дочерний класс [определенный во время выполнения]

То, что вы пытаетесь реализовать здесь, по сути является шаблоном фабричный метод. В статически типизированном, нерефлексивном языке, таком как C++, нет простого способа реализовать фабричный метод общим способом. В большинстве примеров, которые вы найдете, используется тривиальная структура if-else, такая как предложенная вами.

Вы можете увидеть несколько попыток создания общего фабричного метода в другом сообщении, этот ответ кажется подходящим, в частности: https://stackoverflow.com/a/17408318/2079303. Но, как я сказал; не просто.

person eerorika    schedule 10.06.2019
comment
Привет @eeorika, спасибо за ответ. Я действительно вижу проблему, есть ли у вас способ динамического создания экземпляра дочернего класса? Или мой первый подход единственный способ? - person trablazar; 10.06.2019
comment
Спасибо за разъяснение, очень помогло! Я действительно искал шаблоны, но я не думаю, что еще достиг зрелости, чтобы начать их использовать. Тогда я воспользуюсь простым заводским методом, спасибо! - person trablazar; 10.06.2019
comment
Однако @trablazar позаботьтесь о повторном использовании кода из связанных ответов. Как принятый ответ, так и тот, который я связал, напрямую нарушают стандарт, определяя зарезервированные идентификаторы. - person eerorika; 10.06.2019
comment
не волнуйтесь, я реализовал свою собственную, простую версию фабричного метода, я не думаю, что мой проект требует общего фабричного метода, будет достаточно простого переключателя с идентификаторами! Но большое спасибо за предупреждение и за то, что нашли время ответить! - person trablazar; 10.06.2019