Search code examples
c++polymorphismvirtualfactory-pattern

C++ factory made and polymorphism -> invoke subclass's virtual method of an instance accessed by a map's iterator


I have a question about polymorphism.

Here is the significant piece of my code.

I'm sorry if my question is stupid, but i can't understand how can I use polymorphism in this method:

Val_Type Sensor_Map::Get_Last_Value(const std::string &_Nome_Fisico) const

I know that return iter->second.Get_Last_Value(); is not correct, because polymorphism work only if the method is accessed by pointer. I've try to make this:

        const Sensor *S = &(iter->second);
        return S->Get_Last_Value();

but it call the method of Sensor inside the temperature_Sensor method. can the problem be inside factory made when I do Sensori.insert(std::pair<std::string, Sensor>(_Nome_Fisico, *S)); ? In this case, I should store a pointer inside the instance (and change a lot of code :( ), but at the end of SensorMap::Insert() the new instance goes out of scope I can have dangling pointer?

 typedef float Val_Type;
class Sensor
    {
    private:
        Val_Type Last;
    ........
    public:
        virtual Val_Type Get_Last_Value() const;
    ........

    };

class Temperature_Sensor:public Sensor
    {
    ........
    public:
        Val_Type Get_Last_Value() const;
    ........
    };

Val_Type Sensor::Get_Last_Value() const
       {return Last;}  

Val_Type Temperature_Sensor::Get_Last_Value() const 
    { return ((Last * (Tempeartura_Max - Tempeartura_Min) / 1024) + Tempeartura_Min );}


class Sensor_Map
        {
         private:
    ........
            std::map<std::string,Sensor>Sensori;  
    public:
    ........
        inline Val_Type Get_Last_Value(const std::string &_Nome_Fisico) const;
    };



void Sensor_Map::Insert(const std::string &_Nome_Fisico)
        {       
         // FACTORY MADE for sensor type
         char type;
         type=_Nome_Fisico[0];  // first character set the type
         switch(type)
            {
             case 's':
                      { // creo un nuovo sensore base
                        Sensor *S = new Sensor(_Nome_Fisico);
                        Sensori.insert(std::pair<std::string, Sensor>(_Nome_Fisico, *S));
                        delete S;       // deallocazione memoria dinamica
                        break;
                      }         
             case 't':
                      { // creo un nuovo Temperature_Sensor
                        Sensor *S = new Temperature_Sensor(_Nome_Fisico);
                        Sensori.insert(std::pair<std::string, Sensor>(_Nome_Fisico, *S));
                        delete S;       // deallocazione memoria dinamica
                        break;
                      }
            }
        }



Val_Type Sensor_Map::Get_Last_Value(const std::string &_Nome_Fisico) const
    {
    std::map<std::string,Sensor>::const_iterator iter = Sensori.find(_Nome_Fisico);
    if(iter !=Sensori.end())
            {       // <== MY PROBLEM IS HERE ####################################
              // i would like to return the member "Last" of the sensor pointed by iter->second through the more specific methods
            }
    }

thank you very much for your patience, have a nice day


Solution

  • You need to store pointers in your Sensori map in the first place. That is, it should be of type std::map<std::string,Sensor*> or better yet std::map<std::string, std::shared_ptr<Sensor>>. Prefer the second variant as it makes your life much much easier by making you not worry about memory leaks and premature freeing of objects.

    Your program as it stands now has the problem of object slicing when you insert a Temperature_Sensor in Sensori. Your Sensori map stores Sensor objects now, and not objects of any sensor subtype. It's a bit late to take an address of a stored object and hope it will point to a Temperature_Sensor. The Temperature_Sensor you have allocated has been sliced away, it is no more.

    Code like

    Sensor *S = Sensor(_Nome_Fisico);
    Sensori.insert(std::pair<std::string, Sensor>(_Nome_Fisico, *S));
    delete S;       // deallocazione memoria dinamica
    

    could be expressed like this

    Senor S(whatever);
    Sensori.insert(std::pair<std::string, Sensor>(whatever, S));
    // no delete
    

    but it is wrong anyway (see above about object slicing) and you need this

    std::shared_ptr<Sensor> S (std::make_shared<Sensor>(whatever));
    Sensori[whatever] = S; // note no verbose iterator syntax
    // no delete, ever
    

    Last but not least, do not use a leading underscore followed by a capital letter (as in _Nome_Fisico) because such names are reserved for the implementation.