Alcuni dubbi su come gestire una gerarchia di strutture dati e classi simile a quella riportata sotto (i dubbi sono nei commenti messi nel codice riportato):
codice:enum Code: unsigned int {Type1, Type2}; struct BaseStruct { //... Code getType() const = 0; }; struct BaseStruct1: public BaseStruct { //... Code getType() const override { return Type1; }; }; struct BaseStruct2: public BaseStruct { //... Code getType() const override { return Type2; }; }; class Base { public: //...metodi vari virtual void set(BaseStruct* data) = 0; virtual BaseStruct* get() const = 0; virtual Code getType() const = 0; }; class Derived1: public Base { public: //...metodi vari void set(BaseStruct* data) override { if(data)//verifico che data non sia un nullptr { if(data->getType() == Type1)//verifico che data sia del tipo corretto { BaseStruct1* temp = static_cast<BaseStruct1*>(data); //quello che serve per settare i dati membro } else { //se data e' puntatore valido ma di tipo scorretto, due opzioni //1 - lascio che non succeda niente //2 - eccezione } } else { //se arrivo qui, quindi data = nullptr, due opzioni //1 - lascio che non succeda niente //2 - eccezione } }; BaseStruct* get() const override { BaseStruct* temp = nullptr; if(*this)//per fare questo devo aver implementato l'overload dell'operatore bool e quindi aver deciso i casi per cui restituisce true o false { temp = new BaseStruct1(/*passo quello che serve*/); } return temp; } Code getType() const override { return Type1; }; }; class Derived2: public Base { public: //...metodi vari void set(BaseStruct* data) override { if(data)//verifico che data non sia un nullptr { if(data->getType() == Type2)//verifico che data sia del tipo corretto { BaseStruct2* temp = static_cast<BaseStruct2*>(data); //quello che serve per settare i dati membro } else { //se data e' puntatore valido ma di tipo scorretto, due opzioni //1 - lascio che non succeda niente //2 - eccezione } } else { //se arrivo qui, quindi data = nullptr, due opzioni //1 - lascio che non succeda niente //2 - eccezione } }; BaseStruct* get() const override { BaseStruct* temp = nullptr; if(*this)//per fare questo devo aver implementato l'overload dell'operatore bool e quindi aver deciso i casi per cui restituisce true o false { temp = new BaseStruct2(/*passo quello che serve*/); } return temp; } Code getType() const override { return Type2; }; };
creo una classe Factory fatta in modo che crei il corretto oggetto derivato da Base
codice:class Factory { public: static Base* make(BaseStruct* data) { Base* temp = nullptr; if(data) { Code code = data->getType(); switch (code) { case Type1: temp = makeDerived1(data); break; case Type2: temp = makeDerived2(data); break; default: break; }; } return temp; }; private: static Base* makeDerived1(BaseStruct* data); static Base* makeDerived2(BaseStruct* data); };
creo poi una classe che funga da interfaccia per non dovermi preoccupare di che classe concreta derivata da base stia utilizzando.
codice:class Interfaccia { public: //...metodi vari Interfaccia(BaseStruct* data):mBase(Factory::make(data)){};//se data e' nullptr anche mBase contiene nullptr void set(BaseStruct* data) { if(mBase)//prima verifico che effettivamente mBase non sia nullptr, perche' potrei averlo creato cosi' per come e' implementato Factory::make { if(data)//verifico che data non sia un nullptr { if(data->getType() == mBase->getType()) { mBase->set(data); } } else { //qui ho tre opzioni: //1 - faccio niente //2 - mBase.reset(Factory::make(data)); anche se mi sembra il comportamento che crea piu' difficolta di manutenzione, se voglio cambiare // il tipo di mBase, allora e' forse meglio creare un metodo convert(BaseStruct* data) che svolge questo compito //3 - eccezione } } else { //tre opzioni //1 - faccio niente //2 - eccezione //3 - mBase.reset(Factory::make(data)); } }; BaseStruct* get() const { BaseStruct* temp = nullptr; if(mBase) { temp = mBase->get(); } return temp; }; Code getType() { if(mBase) { return mBase->getType(); } else { //due opzioni //1 - nell'Enum Code aggiungo NoType ad esempio e restituisco questo valore //2 - eccezione } }; private: std::unique_ptr<Base> mBase; };
Cosi' su due piedi direi che probabilmente conviene mettere le eccezioni nella gerachia di classi e gestirle nei singoli metodi della classe interfaccia
quindi potrebbe diventare ad esempio
Qualche consiglio? Graziecodice:class Interfaccia { public: //...metodi vari Interfaccia(BaseStruct* data):mBase(Factory::make(data)){};//se data e' nullptr anche mBase contiene nullptr void set(BaseStruct* data) { if(mBase)//prima verifico che effettivamente mBase non sia nullptr, perche' potrei averlo creato cosi' per come e' implementato Factory::make { if(data)//verifico che data non sia un nullptr { if(data->getType() == mBase->getType()) { mBase->set(data); } } else { throw std::MyException("Eccezione"); } } else { throw std::MyException("Altra eccezione"); } catch (std::exception& e) { std::cerr << "exception: " << e.what() << '\n'; } }; BaseStruct* get() const { BaseStruct* temp = nullptr; if(mBase) { temp = mBase->get(); } return temp; }; Code getType() { if(mBase) { return mBase->getType(); } else { //due opzioni //1 - nell'Enum Code aggiungo NoType ad esempio e restituisco questo valore //2 - eccezione } }; private: std::unique_ptr<Base> mBase; };

Rispondi quotando