Visualizzazione dei risultati da 1 a 6 su 6
  1. #1
    Utente di HTML.it L'avatar di ing82
    Registrato dal
    Sep 2014
    Messaggi
    177

    [C++] Metodo con numero parametri variabile

    Ho una famiglia di classi, tutte derivate da una classe astratta.
    In tutte le classi derivate c'e' un metodo set, che però necessita di parametri variabili in numero e tipo.
    Con qualche riga di codice forse mi spiego meglio
    codice:
    class Astratta
    {
      public:
        virtual void set(/*???*/)=0;
    };
    
    class Deriv1: public Astratta
    {
      public:
        //qui set ha bisogno di due double
        void set(const double& par1, const double& par2);
    };
    
    class Deriv2: public Astratta
    {
      public:
        //qui set ha bisogno di tre double
        void set(const double& par1, const double& par2, const double& par3);
    };
    
    class Deriv3: public Astratta
    {
      public:
        //qui set ha bisogno di un vector di Tipo1
        void set(const std::vector<Tipo1>& par);
    };

    La prima soluzione che mi e' venuta in mente e' la seguente: in Astratta implemento piu' metodi set, tutti che lanciano eccezione, mentre nelle classi derivate implemento solo il metodo corretto
    codice:
    class Astratta
    //la classe rimane astratta per altri metodi che qui non sono riportati
    {
      public:
        //i metodi set lanciano eccezione
        virtual void set(const double& par1, const double& par2){/*lancio eccezione*/};
        virtual void set(const double& par1, const double& par2, const double& par3){/*lancio eccezione*/};
        virtual void set(const std::vector<Tipo1>& par){/*lancio eccezione*/};
        //nel caso una nuova classe Derivn abbia dei parametri diversi, sara' necessario aggiungere un ulteriore metodo set
    };
    
    class Deriv1: public Astratta
    {
      public:
        //qui set ha bisogno di due double
        void set(const double& par1, const double& par2) override{/*...*/};
    };
    
    class Deriv2: public Astratta
    {
      public:
        //qui set ha bisogno di tre double
        void set(const double& par1, const double& par2, const double& par3) override{/*...*/};
    };
    
    class Deriv3: public Astratta
    {
      public:
        //qui set ha bisogno di un vector di Tipo1
        void set(const std::vector<Tipo1>& par) override{/*...*/};
    };
    La seconda soluzione e' la seguente: dato che i parametri del metodo set sono un 'qualcosa di omogeneo' dal punto di vista del loro significato,
    potrei creare una ulteriore famiglia di classi, la cui classe astratta da cui derivo le successive diventerebbe quello che vado a passare al metodo set.

    Diciamo che diventerebbe:

    codice:
    class NuovaAstratta
    {
      
    };
    
    class Nuova1: public NuovaAstratta
    {
    
    };

    mentre la prima famiglia di classi potrebbe diventare

    codice:
    class Astratta
    {
      public:
        virtual void set(const NuovaAstratta*& puntatore)=0;
    };
    
    class Deriv1: public Astratta
    {
      public:
        //qui set ha bisogno di due double
        void set(const NuovaAstratta*& puntatore) override;
    };
    
    class Deriv2: public Astratta
    {
      public:
        //qui set ha bisogno di tre double
        void set(const NuovaAstratta*& puntatore) override;
    };
    
    class Deriv3: public Astratta
    {
      public:
        //qui set ha bisogno di un vector di Tipo1
        void set(const NuovaAstratta*& puntatore) override;
    };
    In modo analogo potrebbe funzionare quindi per il metodo get, che restituirebbe anch'esso sempre lo stesso tipo, NuovaAstratta.

    Ma qui mi blocco nuovamente, perche' mi sembra che abbia semplicemente spostato il problema: avro' anche qui set e get che hanno lo stesso problema di parametri passati o restituiti diversi...

    C'e' qualche via d'uscita, senza dover implementare infiniti metodi set e get?

    In realta' pensavo alla possibilita' di usare 'i tre puntini' (stdarg), ma da quello che ho capito e sperimentato, tra i parametri opzionali non digerisce i vector...e sistemerei solamente il metodo set.

    Nella speranza che Shodan legga, tra i Design Pattern mi sembrava potesse darmi una mano Bridge, ma non mi sblocco...

    Grazie a tutti.

  2. #2
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    Dunque, partiamo dal fondo:
    In realta' pensavo alla possibilita' di usare 'i tre puntini' (stdarg), ma da quello che ho capito e sperimentato, tra i parametri opzionali non digerisce i vector...e sistemerei solamente il metodo set.
    Le funzioni variadic del C lasciale perdere se usi il C++. Non che si possano usare, ma fanno a pugni con gli oggetti per cui usarle può portare pena infinita.
    mi sembrava potesse darmi una mano Bridge,
    No, Bridge qui non c'entra nulla; anche perché alla fine il Bridge è Interfaccia <- implementazione.
    Dato che passi anche un Tipo1, non vedo altro modo che usare una variante di Memento che Eckel definisce Messenger (non è nella copia del GOF che ho, ma si trova nei suoi libri).
    Trovandomi in un caso simile ho optato per la seconda soluzione risolvendo più o meno così:
    ( onestamente farei a meno di metodi get/set per la struct/class params visto che è un qualcosa usa e getta).
    Per la get risolvi più o meno allo stesso modo.
    codice:
    struct params {
        enum {
            for_deriv1, for_deriv2, for_deriv3   
        };
     virtual int rtti() = 0;
    };
    
    struct params_deriv1 : public params {
        double value1;
        int rtti() { return for_deriv1; }
    }
    
    struct params_deriv2 : public params {
        double value1;
        double value2;
        int rtti() { return for_deriv2; }
    }
    
    template <typename Tipo1> // se Tipo1 non è un typedef
    struct params_deriv3 : public params {
        std::vector<Tipo1> vec;
        int rtti() { return for_deriv1; }
    }
    
    
    /* ....  */
    
    
    class Deriv1 : public Astratta {
        public:
            void set(const params* p) override {
                if (p != nullptr) {
                    if (p->rtti() == params::for_deriv1) {
                        const params_deriv1* pd1 = static_cast<params_deriv1*>(p);
                        this->param1 = pd1->value1;
                    }
                } else {
                    /* faccio niente o imposto un parametro di default per param1 o eccezione */
                }
            }
    };
    
    class Deriv2 : public Astratta {
        public:
            void set(const params* p) override {
                if (p != nullptr) {
                    if (p->rtti() == params::for_deriv2) {
                        const params_deriv2* pd2 = static_cast<params_deriv2*>(p);
                        this->param1 = pd2->value1;
                        this->param2 = pd2->value2;
                    }
                } else {
                    /* faccio niente o imposto un parametro di default per param1 o eccezione */
                }
            }
    };
    
    class Deriv3 : public Astratta {
        public:
            void set(const params* p) override {
                if (p != nullptr) {
                    if (p->rtti() == params::for_deriv3) {
                        const params_deriv3* pd2 = static_cast<params_deriv3*>(p);
                        this->vec = std::move(pd2->vec);
                    }
                } else {
                    /* faccio niente o imposto un parametro di default per param1 o eccezione */
                }
            }
    };
    
    /* ... */
    
    params_deriv2 pd2;
    pd2.value1 = 1.0;
    pd2.value2 = 2.0;
    
    Deriv2 d2;
    d2.set(&pd2);
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

  3. #3
    Utente di HTML.it L'avatar di ing82
    Registrato dal
    Sep 2014
    Messaggi
    177
    Ho provato a completare quanto postato, in modo da avere un esempio completo e funzionante, anche col metodo get, anche per i posteri che leggeranno.

    Ecco sotto il risultato dell'esercizio: mi resta abbastanza oscuro il perche' abbia dovuto mettere const anche ai metodi rtti, altrimenti non compilava.

    Le funzioni setDerivxxx e getDerivxxx sono state scritte solo per ridurre le dimensioni del main.

    Se non dico inesattezze, per come viene istanziato params_deriv3, potrebbe essere necessario definire un costruttore di copia per Prova.

    Intanto ho anche scoperto l'esistenza di std::move: sara' meglio dare un'occhiata piu' approfondita alla documentazione dei vector, e anche alla documentazione in generale...

    Grazie infinite nuovamente.

    codice:
    #include <iostream>
    #include <string>
    #include <vector>
    
    //semplice classe per testare anche coi vector
    class Prova
    {
     public:
       Prova(const int& number):val(number){};
       int get() const {return val;};
     private:
       int val;
    };
    
    
    //inizio della famiglia di struct
    struct params {
        enum {
            for_deriv1, for_deriv2, for_deriv3   
        };
     virtual int rtti() const = 0;
    };
    
    struct params_deriv1 : public params {
        params_deriv1(const double& v1):value1(v1){};
        int rtti() const { return for_deriv1; };
        double value1;
    };
    
    struct params_deriv2 : public params {
        params_deriv2(const double& v1, const double& v2):value1(v1),value2(v2){};
        int rtti() const { return for_deriv2; };
        double value1;
        double value2;
    };
    
    //template <typename Tipo1> // se Tipo1 non è un typedef
    struct params_deriv3 : public params {
        params_deriv3(const std::vector<Prova>& v1):vec(v1){};
        int rtti() const { return for_deriv3; };
        std::vector<Prova> vec;
    };  
    
    
    
    
    class Astratta {
        public:
            virtual ~Astratta(){};
            virtual void set(const params* p) = 0;
            virtual params* get() const = 0;
    };
    
    class Deriv1 : public Astratta {
        public:
            void set(const params* p) override {
                if (p != nullptr) {
                    if (p->rtti() == params::for_deriv1) {
                        const params_deriv1* pd1 = static_cast<const params_deriv1*>(p);
                        this->param1 = pd1->value1;
                    }
                } else {
                    // faccio niente o imposto un parametro di default per param1 o eccezione 
                }
            };
            
            params* get() const override {
                params* p = new params_deriv1(param1);
                 return p;
            };
        private:
            double param1;
    };
    
    class Deriv2 : public Astratta {
        public:
            void set(const params* p) override {
                if (p != nullptr) {
                    if (p->rtti() == params::for_deriv2) {
                        const params_deriv2* pd2 = static_cast<const params_deriv2*>(p);
                        this->param1 = pd2->value1;
                        this->param2 = pd2->value2;
                    }
                } else {
                    // faccio niente o imposto un parametro di default per param1 o eccezione 
                }
            };
            params* get() const override {
                 params* p = new params_deriv2(this->param1,this->param2);
                 return p;
            };
        private:
            double param1;
            double param2;
    };
    
    class Deriv3 : public Astratta {
        public:
            void set(const params* p) override {
                if (p != nullptr) {
                    if (p->rtti() == params::for_deriv3) {
                        const params_deriv3* pd2 = static_cast<const params_deriv3*>(p);
                        this->_vector = std::move(pd2->vec);
                    }
                } else {
                    // faccio niente o imposto un parametro di default per param1 o eccezione 
                }
            };
            params* get() const override {
                params* p = new params_deriv3(_vector);
                return p;
            };
        private:
            std::vector<Prova> _vector;
    };
    
    //possibile funzione per settare i valori di Astratta nel caso sia di tipo Deriv1
    void setDeriv1(Astratta*& astratta)
    {
        double p1;
        std::cout<<"\nInserire il primo valore: ";
        std::cin>>p1;
        params* p = new params_deriv1(p1);
        astratta->set(p);
    };
    //possibile funzione per prelevare i valori di Astratta nel caso sia di tipo Deriv1
    void getDeriv1(Astratta*& astratta)
    {
        params* p = astratta->get();
        params_deriv1* pd1 = static_cast<params_deriv1*>(p);
        std::cout<<"\nIl valore memorizzato in astratta e' "<<pd1->value1<<"\n";
    };
    
    //possibile funzione per settare i valori di Astratta nel caso sia di tipo Deriv2
    void setDeriv2(Astratta*& astratta)
    {
        double p1,p2;
        std::cout<<"\nInserire il primo valore: ";
        std::cin>>p1;
        std::cout<<"\nInserire il secondo valore: ";
        std::cin>>p2;
        params* p = new params_deriv2(p1,p2);
        astratta->set(p);
    };
    //possibile funzione per prelevare i valori di Astratta nel caso sia di tipo Deriv2
    void getDeriv2(Astratta*& astratta)
    {
        params* p = astratta->get();
        params_deriv2* pd2 = static_cast<params_deriv2*>(p);
        std::cout<<"\nIl primo valore memorizzato in astratta e' "<<pd2->value1<<"\n";
        std::cout<<"\nIl secondo valore memorizzato in astratta e' "<<pd2->value2<<"\n";
    };
    
    //possibile funzione per settare i valori di Astratta nel caso sia di tipo Deriv3
    void setDeriv3(Astratta*& astratta)
    {
        std::vector<Prova> pippo;
        unsigned int n;
        std::cout<<"\nInserire il numero di valori del vector: ";
        std::cin>>n;
        for(unsigned int i = 0; i < n ; i++)
        {
            pippo.push_back(i);
        }
        params* p = new params_deriv3(pippo);
        astratta->set(p);
    };
    //possibile funzione per prelevare i valori di Astratta nel caso sia di tipo Deriv3
    void getDeriv3(Astratta*& astratta)
    {
        params* p = astratta->get();
        params_deriv3* pd3 = static_cast<params_deriv3*>(p);
        for(unsigned int i = 0; i < pd3->vec.size(); i++)
        {
          std::cout<<"\nElemento numero "<<i+1<<": "<<pd3->vec[i].get()<<"\n";
        }
    };
    
    int main()
    {
      Astratta* a = new Deriv1;
      setDeriv1(a);
      getDeriv1(a);
      delete (a);
      a = new Deriv2;
      setDeriv2(a);
      getDeriv2(a);
      delete(a);
      a = new Deriv3;
      setDeriv3(a);
      getDeriv3(a);
    }

  4. #4
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    Se non dico inesattezze, per come viene istanziato params_deriv3, potrebbe essere necessario definire un costruttore di copia per Prova.
    No, non serve. Il costruttore di copia si renderebbe necessario se Prova contenesse un puntatore. Se ti limiti a usare dati normali (int, float etc...) o classi STL non è necessario implementare un costruttore di copia.
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

  5. #5
    Utente di HTML.it L'avatar di ing82
    Registrato dal
    Sep 2014
    Messaggi
    177
    Quote Originariamente inviata da shodan Visualizza il messaggio
    No, non serve. Il costruttore di copia si renderebbe necessario se Prova contenesse un puntatore. Se ti limiti a usare dati normali (int, float etc...) o classi STL non è necessario implementare un costruttore di copia.
    Ok, era quello che avevo in mente, ma che hai espresso molto meglio...Prova era una classe buttata li per prova, appunto.

    Per quanto riguarda il const nel metodo rtti?

    Grazie

  6. #6
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    Per quanto riguarda il const nel metodo rtti?
    Perché nel metodo set il puntatore passato è const per cui anche la funzione dev'essere const.
    Dimenticanza mia non averlo messo.
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved.