2014-11-15 2 views
1

У меня возникла проблема с std :: swap. Мне нужно поменять объект. Этот объект освобождает память в своем деструкторе. Я написал конструктор перемещения и оператор назначения перемещения, который копирует указатель на эту память. По умолчанию конструктор устанавливает указатель на NULL.Нежелательный вызов деструктора в std :: swap

Конечно, у меня есть обычный конструктор и оператор присваивания, но они выделяют и копируют память, что явно не то, что я хочу для моей операции свопинга.

Когда я вызываю std :: swap, он создает временный объект из _Left, используя мой конструктор перемещения. Затем он использует мой оператор назначения перемещения для перемещения _Right в _Left, и, наконец, он перемещает объект temp в _Right.

Все это выглядит хорошо, когда вы попадаете на дно std :: swap. Однако, когда вы выходите из нижней части, деструктор для временного объекта запускается, освобождая память, которую ожидает объект _Right.

Каков нормальный приемлемый способ сделать это? Я надеялся избежать необходимости писать функции обмена, поскольку это точка перемещения конструкторов/перенос операторов присваивания. Должен ли я использовать свой собственный своп(), чтобы избежать этого?

+1

Показать свою реализацию. – Jarod42

+1

Показывая нам, что реализация специальных функций с помощью специального перехода была бы намного проще, чем написать несколько абзацев о проблеме. Во всяком случае, похоже, что вы не устанавливаете указатель в исходном объекте на «nullptr» после перехода от него. – Praetorian

ответ

0

Хорошо, после намного большего изучения, я понимаю свою фундаментальную проблему и решение.

Короткая версия:
В своем ходе конструктора, копировать значения из исходного объекта, а затем установить их значение, они были бы, если ваш конструктор по умолчанию было бежать.

Long Version:
При создании конструктора перемещения или перемещений оператора присваивания, вы должны покинуть объект быть назначен из в состоянии по умолчанию, которые могут быть деградировавшим. Перестановка не делает этого. Вместо этого вам нужно сначала украсть ресурсы из исходного объекта, а затем установить члены исходного объекта в состояние, в котором они будут находиться, если был запущен конструктор.

Один пример, когда вы обмениваете землю в горячей воде, где вы имеете дело с указателем, как и я. Вы не можете копировать или заменять его. Если вы скопируете указатель, тогда значение указателя будет по-прежнему находиться в исходном объекте, когда оно будет разрушено. Если вы поменяете указатель, это будет неинициализированное значение, которое, вероятно, приведет к краху вашего деструктора. Вместо этого вы должны скопировать значение указателя, а затем установить указатель в исходном объекте в значение NULL. Конечно, вы должны проверить NULL в деструкторах при выпуске памяти.

Если один из членов, которых вы крадете, является экземпляром класса, которым вы управляете, тогда вы должны использовать std :: move, чтобы скопировать его, так как это вызовет его оператор присваивания перемещения, а также оставит его разрушаемым.

Бонус:
Не изобретайте велосипед. Просто вызовите оператор назначения перемещения из вашего конструктора перемещения.

void CObject::CObject(CObject&& other) 
{ 
    *this = std::move(other); 
} 
CObject& CObject::operator = (CObject&& other) 
{ 
    // m_pResource is a pointer. 
    // Copy the value. 
    m_pResource = other.m_pResource; 
    // Set other to default state so the destructor doesn't free it. 
    other.m_pResource = NULL; 

    // m_iCount is an int who's value does not matter in the destructor. 
    m_iCount = other.m_iCount; 

    // m_Obj is a class instance. 
    // Invoking move semantics will use its move assignment operator if it has one. 
    m_Obj = std::move(other.m_Obj); 

    return *this; 
} 
+0

«вы должны проверить NULL в деструкторах при освобождении памяти», no delete nullptr; is fine – paulm

+0

Вы правы, что вы можете вызвать delete/free по нулевому указателю. Однако, если вы делаете что-то большее, чем для освобождения ресурсов, тогда имеет смысл просто сделать чек. Для меня это просто чувствует себя лучше. – Jamie

4

Операция перемещения должна оставлять объект, перемещаемый из разрушаемого состояния, которое может быть другого состояния, чем то, как оно попало в ход.

Если я правильно понимаю проблему, это звучит так, как движок move-ctor вашего объекта должен установить указатель (ы) в объекте, перемещенном из чего-то другого, кроме значений, с которыми они пришли. Последующий dtor на тех теперь перемещенных объектах должен оставить память, которую они когда-то называли отдельно.

+0

Разве это не указано * состояние, а не * разрушаемое *? –

+5

@remyabel: Чтобы быть в любом случае пригодным для использования, государство должно быть «неуказанным, но действительным», где «действительный» означает, по крайней мере, «разрушаемый». Все стандартные типы библиотек предоставляют эту гарантию; определяемый пользователем тип, который обычно не может быть безопасно перемещен. –

Смежные вопросы