Reputation: 500
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.
Upvotes: 0
Views: 737
Reputation: 1621
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()));
}
Upvotes: 3
Reputation: 942
To implement a deep copy you can add an abstract function virtual FieldBase * FieldBase::clone() = 0
;
The implementation FieldBase* Field<T>::clone()
would then return new Field<T>(*this)
;
The only thing left is to iterate over each entry of Store::m_data
and call it->second->clone()
;
Upvotes: 2