views:

80

answers:

6

Hello,

I have another problem I can't seem to solve..., or find on this site...

I have an object (called DataObject) with a map, declared as follows:

std::map<size_t, DataElement*> dataElements;

Now i have a copy function (used in the copy constructor):

void DataObject::copy(DataObject const &other) {

    //here some code to clean up the old data in this object...

    //copy all the elements:
    size = other.getSize();
    for(size_t i = 0; i < size; ++i) {
            DataElement* dat = new DataElement(*other.dataElements[i]);
            dataElements[i] = dat;
    }

}

This doesn't compile, since dataElements[i] is not possible on a const object. How do I make a deep copy of all the elements in the map that is owned by a const object?

I know that the find() function is possible on a const map, but then how do I get to the actual object that I want to copy?

A: 

Don't have much time to answer now so this will be brief. There is a copy-constructor for map, but it won't do a deep copy. You want to use iterators (map.begin(), map.end()). *Iter will give you a pair object, so you can do (*iter).first and/or (*iter).second. (Or something like that... It's been a while...)

Ref: http://www.sgi.com/tech/stl/Map.html

Mr.Ree
+4  A: 
std::map<size_t, DataElement*>::const_iterator it = other.dataElements.begin();
while(it != other.dataElements.end())
{
    dataElements[it->first] = new DataElement(*(it->second));
    ++it;
}

I'm almost positive this should work.

PigBen
It will work but if your map has an element already at that location you will overwrite it thus losing the pointer you have to delete (and will get a leak). You should check for this first.
CashCow
If "...some code to clean up the old data..." empties this->dataElements, this code is fine.
Useless
It needs to delete dataElements[it->first] if it existed. This would work: void replace(DataElement* old=newv } then put replace(dataElements[it->first],new DataElement(*(it->second)));
CashCow
@CashCow -- As Useless said, according to OP's comment, "//here some code to clean up the old data in this object..." the old data was already cleaned up.
PigBen
@PigBen so we are assuming the map we are writing to is empty, in which case it will work fine.
CashCow
I indeed clean the map (and delete the referred objects) first. This works very well!
A: 

Just one observation :- You are giving direct access to the dataElements. (other.dataElements). Keep dataElements private and then give method like GetDataElement.

Manoj R
No, it is the same class. It probably is private but another instance of the same class will have access, as private is not a "per object" access specifier.
CashCow
Dont do that. Get/Set methods are the antithesis of encapsulation. Also note an class is **always** a friend of itself. So it can easily access the other objects elements. This does **NOT** break encapsulation as the objects are meant to know about each other (as they are the same type how can they not know the implementation details of each other).
Martin York
I indeed always learned that it is okay to do that in a copyconstructor (or a function like this, that is used in the copy constructor). The members of DataObject and DataElement are of course private.
A: 

Use a const iterator.

stonemetal
A: 

You need to use std::transform. This does a copy whilst also performing a function on each element. In your case a deep copy of the value.

This will therefore do as a transformer:

class DeepCopyMapPointer
{
   typedef std::map<size_t, DataElement*> map_type;
   typedef map_type::value_type value_type;

public:
   value_type operator()( const value_type & other ) const
   {
      return value_type(other.first, new DataElement(*other.second) );
   }
};

void DataObject::copy(DataObject const &other) 
{
   std::transform(other.dataElements.begin(), other.dataElements.end(),
      std::inserter( dataElements, dataElements.end() ), DeepCopyMapPointer() );
}

It's not quite that simple because if you do duplicate an element and your insert fails as a result you will get a leak. You could get round that by writing your own inserter instead of std::inserter... a bit tricky but that's your next exercise.

CashCow
+1  A: 

Since your map just has integer keys from 0 to n - 1, just change your container type to a vector, and your current code should work nicely (you'll need to resize the destination container to make sure there's enough room available).

If you need to use map for some reason (existing API?), as you discovered operator[] has only a non-const version.

Instead use a const_iterator approach (upvoted and taken from @PigBen's answer):

std::map<size_t, DataElement*>::const_iterator it = other.dataElements.begin();
while(it != other.dataElements.end())
{
    dataElements[it->first] = new DataElement(*(it->second));
    ++it;
}
Mark B