openbas2
openbas2

Reputation: 263

How to (deep)copy a map from a const object

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?

Upvotes: 7

Views: 13166

Answers (6)

Mark Kahn
Mark Kahn

Reputation: 1158

for (auto& kv : other.dataElements) {
            dataElements[kv.first] = new DataElement(*kv.second);
        }

Upvotes: 0

Mark B
Mark B

Reputation: 96241

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;
}

Upvotes: 1

CashCow
CashCow

Reputation: 31435

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.

Upvotes: 2

Manoj R
Manoj R

Reputation: 3247

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

Upvotes: -1

Benjamin Lindley
Benjamin Lindley

Reputation: 103693

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.

Upvotes: 10

Mr.Ree
Mr.Ree

Reputation: 8418

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

Upvotes: 0

Related Questions