Правила программирования на Си и Си++



         

144. Не вызывайте конструкторов из операции operator=( )


Хотя это правило говорит о перегруженном присваивании, на самом деле оно посвящено проблеме виртуальных функций. Соблазнительно реализовать operator=()

следующим образом:

class some_class

{

public:

   virtual

     ~some_class( void                               );

      some_class( void                               );

      some_class( const some_class &r );

   const some_class &operator=( const some_class &r );

};

const some_class &operator=( const some_class &r )

{

   if( this

!= &r )

   {

      this->~some_class();

      new(this) some_class(r);

   }

   return *this;

}

Этот вариант оператора new инициализирует указываемый this объект как объект some_class, в данном случае из-за аргумента r

используя конструктор копии.12

Есть серьезные причины не делать показанное выше. Во-первых, это не будет работать после наследования. Если вы определяете:

class derived : public some_class

{

public:

   ~derived();

   // Предположим, что генерированная компилятором операция

   // operator=() выполнится за операцией operator=() базового

   // класса.

}

Вследствие того, что деструктор базового класса определен (правильно) как виртуальный, обращение предыдущего базового класса к:

this->~some_class()

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

this->some_class::~some_class();

Явное упоминание имени класса — some_class:: в этом примере —

подавляет механизм виртуальной функции. Функция вызывается, как если бы она не была виртуальной.

Деструктор не является единственной проблемой. Рассмотрим простое присваивание объектов производного класса:

derived d1, d2;

d1 = d2;

Операция производного класса operator=()

(вне зависимости от того, генерируется она компилятором или нет) образует цепочку с operator=()

базового класса, который в настоящем случае использует оператор new() для явного вызова конструктора базового класса. Конструктор, тем не менее, делает значительно больше, чем вы можете видеть в определении. В частности, он инициализирует указатель таблицы виртуальных функций так, чтобы он указывал на таблицу его класса. В текущем примере перед присваиванием указатель vtable




Содержание  Назад  Вперед