2015-01-31 3 views
3

Так что я собирался через кусок текста в C++ и наткнулся на следующий фрагмент кода:Выполнение задания на классы

class example 
{ 
    int dataMember; 

public: 
    example& assign(const example& source) 
    { 
     if(this!=&source) 
     { 
      this->~example(); 
      new (this) example(source); 
     } 


    } 

}; 

Хорошо, так что я пытаюсь расшифровать то, что делает эта функция правопреемником. Что я еще не понял:

  1. Функция принимает постоянную ссылку экземпляра класса и возвращает ссылку на класс.

  2. Внутри блока if, во-первых, деструктор вызывается для текущего экземпляра (насколько я знаю, текущий объект уничтожается и память освобождается).

Теперь главный вопрос:

new (this) example(source)

Эта линия беспокоит меня. Что здесь происходит? Если меня попросят угадать, я бы сказал, что создается новый объект и назначается как текущий объект, так как я могу сделать вывод из ключевого слова this.

Может ли это прояснить это? Как здесь обстоят дела?

Этот метод безопасен? (Если распределение происходит динамически, программисту необходимо будет освободить его в будущем вручную)

Спасибо.

+3

это называется [размещение нового] ​​(http://stackoverflow.com/questions/222557/what-uses-are-there-for-placement-new) – bolov

+0

@bolov, удивительное, это точно, что я искал. Теперь я буду искать в Интернете и узнать больше об этом. –

+1

Это ужасный код. – milleniumbug

ответ

3

Что вы видите, это попытка повторного использования кода. Идея состоит в том, чтобы использовать конструктор копирования для реализации оператора присваивания (или, в данном случае, функции назначения).

Во-первых, чтобы избавиться от легкого материала:

  • if гарантирует, что назначение самостоятельно (например, x.assign(x)) обрабатывается правильно. Это необходимо, поскольку реализация зависит от того, что изменение *this не изменилось. source. Сравнивая с адресом другого объекта, это проверяет объекты на равенство, но для однообразия.

  • Функция отсутствует return *this;, но вы, вероятно, заметили это уже.

Теперь к двум оставшимся линиям:

this->~example(); 

Явное вызывает деструктор класса example. После этой строки указатель this больше не указывает на объект, а на неинициализированную память sizeof(example).

new (this) example(source); 

Есть так называемый placement new, который делает не выделяет память, а просто создает новый example на место, на который указывает this вызывая его конструктор копирования. В некотором роде это синтаксис для явного вызова конструктора без выделения какой-либо памяти.

Обратите внимание, что это повторно использует память, которую *this хранит ранее, независимо от того, где это: это может быть даже, например, a virtual база элемента динамически распределенной массива ...

Что касается безопасности: на практике это безопасно (хотя и уродливое и, возможно, не очень эффективное), пока ни конструктор, ни деструктор не выбрасывают. Тем не менее, вы можете столкнуться с проблемами при получении класса, который пытается так скрытно.

Если вы хотите знать, самый простой способ, чтобы написать оператор присваивания/функцию, которая повторно использует конструктор копирования, проверьте this question, которая в основном сводится к:

example& assign(example source) 
{ 
    swap(*this, source); 
    return *this; 
} 

Наиболее важные различия:

  • Он использует конструктор перемещения, где это применимо.
  • Этот код является безопасным исключением, если конструкторы copy/move и swap являются безопасными для исключения (которые они всегда должны быть).
  • Он немного менее эффективен для самостоятельного назначения, так как он выполняет копию вместо того, чтобы распознавать самоопределение.
+0

Можете ли вы расширить свой последний комментарий, о производных классах? Вы имеете в виду вызов деструктора в этом или вызов нового конструктора размещения? Или оба? –

+0

@AaronMcDaid Размещение-новый будет [срез] (http://stackoverflow.com/questions/274626/what-is-object-slicing), и если деструктор «виртуальный», ну, вы уничтожите больше, чем вы ожидаете , Кроме того, он мог бы использовать базовые классы 'virtual' ... –

+0

@AaronMcDaid В принципе: у меня не было бы шаров, чтобы полагаться на код, который происходит от такого класса, независимо от того, работает он или нет.(Это относится только к выходу из него, а не к составу.) –

1

Эта часть уничтожает объект:

this->~example(); 

Это не освобождает память, хотя, на память, которая выделяется из-за вызов деструктора, за исключением. Затем он переходит с вызовом «размещения нового»:

new (this) example(source); 

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

Что касается безопасности этого, это, по крайней мере, опасно. Если после уничтожения объекта вы не создадите новый объект, у вас будет странный объект зомби, который будет уничтожен во второй раз (вызывая неопределенное поведение), когда закончится его нормальное время жизни.

+0

Должен ли я затем явно вызвать деструктор, чтобы убедиться, что утечки памяти нет? –

+1

Нет. Обычно область создает один вызов конструктора, а другой - деструктору. Теперь этот код вводит вызов деструктора, за которым следует вызов конструктора между ними. Таким образом, у вас есть две пары конструктора/деструктора. –

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