Search code examples
c++templatescopy-constructordeep-copyassignment-operator

Assignment operator and copy constructor for class containing base class pointer to derived templated class


Apologies for the long title. I am trying to write an assignment operator and a copy constructor for a class which I call Store.

The purpose of Store is hold other structures (e.g. integers, floats), identified by a std::string, which can be added/retrieved from the Store. This is implemented with a std::map, with a std::string as the 'key' in the map and the particular structure as the 'value' in the map.

Store is defined as follows:

    class Store {

    public:

      Store() {}

      template <class T>
      const T& get(const std::string& key) const;

      template <class T>
      void put(const std::string& key, const T& value, const bool& overwrite = false);

    private:

      std::map<std::string,FieldBase*> m_data;

    };

where FieldBase is defined as follows:

    class FieldBase {

    public:

      FieldBase() {}
      virtual ~FieldBase() {}

    }

and a class derived from FieldBase called Field is defined as follows:

    template <class T>
    class Field : public FieldBase {

    public:

      Field(const T& value) : m_value(value) {}

      template <class U>
      Field(const Field<U>& other) : m_value( U(other.m_value) ) {}

      template <class U>
      Field& operator=(const Field<U>& other) 
      { 
        m_value = U(other.m_value);
        return *this;
      }

      virtual ~Field() {}

      const T& get() const { return m_value ; }

     private:

       T m_value;

    };

The functionality in Store to add and retrieve, is defined below. To retrieve, one uses Store::get() :

    template <class T>
    const T& Store::get(const std::string& key) const
    {

      std::map<std::string,FieldBase*>::const_iterator it = m_data.find(key);

      if ( it == m_data.end() ) {
        std::cout << "Field with name " << key <<" doesn't exist!" << std::endl;
        throw 0;
      }

      Field<T>* field = dynamic_cast<Field<T>*>(it->second);
      if ( field == 0 ) {
        std::cout << "Field with name " << key << " doesn't have correct type!" << std::endl;
        throw 0;
      }

      return field->get();

    }

and to add, one uses Store::put()

    template <class T>
    void Store::put(const std::string& key, const T& value, const bool& overwrite)
    {

      std::map<std::string,FieldBase*>::iterator it = m_data.find(key);

      if ( it != m_data.end() ) {
        if ( ! overwrite ) {
          std::cout << "Field with name " << key << " doesn't exist!" << std::endl;
          throw 0;
        }
        else {
          delete it->second;
          it->second = 0;
        }
      }

      Field<T>* field = new Field<T>(value);
      m_data[key] = field;

    }

So, having described the classes and their interactions, I finally arrive at the question:

How should the copy constructor and assignment operator look for Store?

Obviously, one should iterate over the std::map<std::string,FieldBase*> and somehow fill the target map by deep copying the objects, but my problem is that I don't know how to determine the type of the Field which hides beneath each FieldBase pointer...

    // How should these be implemented ???
    Store::Store(const Store& other); 
    Store& Store::operator=(const Store& other);

Any help is much appreciated.


Solution

  • You should look at the clone pattern.

    http://en.wikipedia.org/wiki/Cloning_(programming)

    What you do is add a pure abstract member function to FieldBase which is defined in the most derived types (Field).

    So:

    virtual FieldBase* clone() const = 0; //! This goes in FieldBase
    
    FieldBase* clone() const { return new Field<T>(m_value); } //! This goes in Field
    

    Then in the copy constructor you iterate over the map and clone the underlying values before inserting them in the new instance's map.

    Something like this:

    Store(const Store& other)
    {
        typedef std::map<std::string, FieldBase*> StoreMap;
        for (StoreMap::const_iterator it(other.m_data.begin()); it != other.m_data.end(); ++it)
            m_data.insert(std::make_pair(it->first, it->second->clone()));
    }