2010-10-27 3 views
2

У меня есть еще одна проблемы я не могу решить ... или найти на этом сайте ...Как (глубоко) скопировать карту из константного объекта

У меня есть объект (так называемый DataObject) с картой, объявляется следующим образом:

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

Теперь у меня есть функция копирования (используется в конструкторе копирования):

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

} 

Это не компилируется, так как dataElements [я] не представляется возможным на объект const. Как сделать глубокую копию всех элементов на карте, принадлежащей объекту const?

Я знаю, что функция find() возможна на карте const, но как мне добраться до фактического объекта, который я хочу скопировать?

+0

, что это ошибка? Что такое DataElement? Какие параметры использует его конструктор? –

+1

Ваша функция не отмечена как const, так как данныеElements const? – Puppy

+1

Если ваши ключи работают от 0 до N, это почти наверняка связано с тем, что карта является неправильной коллекцией для того, что вы пытаетесь сделать. Почему бы просто не использовать вектор или deque? – CashCow

ответ

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

Я почти уверен, что это должно работать.

+0

Он будет работать, но если ваша карта имеет элемент уже в этом месте, вы перезапишете его, тем самым потеряв указатель, который вы должны удалить (и получит утечку). Сначала вы должны проверить это. – CashCow

+1

Если «... код для очистки старых данных ...» опустошает this-> dataElements, этот код в порядке. – Useless

+0

Для этого необходимо удалить dataElements [it-> first], если он существует. Это будет работать: void replace (DataElement * & old, DataElement * newv) {удалить старый; old = newv}, затем поместите replace (dataElements [it-> first], новый DataElement (* (it-> second))); – CashCow

0

У меня нет времени ответить, так что это будет кратким. Для карты есть экземпляр-конструктор, но он не будет делать глубокую копию. Вы хотите использовать итераторы (map.begin(), map.end()). * Iter даст вам парный объект, поэтому вы можете сделать (* iter) .first и/или (* iter). Секунд. (Или что-то в этом роде ... Это было в то время как ...)

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

-1

Просто одно наблюдение: - Вы даете прямой доступ к даннымElements. (other.dataElements). Храните dataElements частным образом, а затем дайте метод GetDataElement.

+0

Нет, это тот же класс. Он, вероятно, является закрытым, но другой экземпляр того же класса будет иметь доступ, поскольку private не является спецификатором доступа для каждого объекта. – CashCow

+1

Не делайте этого. Методы Get/Set являются антитезой инкапсуляции. Также обратите внимание, что класс ** всегда ** является другом. Таким образом, он может легко получить доступ к другим элементам объектов. Это делает ** НЕ ** прерывать инкапсуляцию, поскольку объекты должны знать друг о друге (поскольку они одного типа, как они могут не знать детали реализации друг друга). –

+0

Я действительно всегда узнал, что это нормально делать в copyconstructor (или такой функции, которая используется в конструкторе копирования). Члены DataObject и DataElement, конечно, являются частными. – openbas2

1

Вам необходимо использовать std :: transform. Это делает копию, одновременно выполняя функцию для каждого элемента. В вашем случае - глубокая копия значения. Поэтому

Это будет делать как трансформатор:

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

Это не так просто, потому что если вы дублировать элемент и ваша вставка терпит неудачу в результате вы получите утечку. Вы можете обойти это, написав свой собственный inserter вместо std :: inserter ... немного сложнее, но это ваше следующее упражнение.

1

Поскольку ваша карта имеет целые ключи от 0 до n - 1, просто измените тип контейнера на вектор, и ваш текущий код должен работать хорошо (вам нужно будет изменить размер контейнера назначения, чтобы убедиться, что есть достаточно места) ,

Если вам нужно использовать map по какой-либо причине (существующий API?), Как вы обнаружили, operator[] имеет только неконстантную версию.

Вместо использовать const_iterator подход (upvoted и взятый из @ ответ PigBen в):

std::map<size_t, DataElement*>::const_iterator it = other.dataElements.begin(); 
while(it != other.dataElements.end()) 
{ 
    dataElements[it->first] = new DataElement(*(it->second)); 
    ++it; 
} 
Смежные вопросы