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

       

Не возвращайте ссылки на память, выделенную оператором new


Каждый вызов new должен сопровождаться delete — подобно malloc() и free(). Я иногда видел людей, старающихся избежать накладных расходов от конструкторов копии перегруженной бинарной операции подобным образом:

const some_class some_class::operator+( const some_class r )

const

{

   some_class *p = new some_class;

   // ...

   return *p;

}

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

some_class a, b, c;

c = a + b;

то a + b возвращает объект, а не указатель. Единственным способом получить указатель, который вы можете передать в оператор delete, является:

some_class *p;



c = *(p = (a + b));

Это даже страшно выговорить. Функция operator+() не может прямо возвратить указатель. Если она выглядит подобным образом:

const some_class *some_class::operator+( const some_class r )

const

{

   some_class *p = new some_class;

   // ...

   return p;

}

то вы должны записать:

c = *(p = a + b);

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

const some_class some_class::operator+( const some_class r )

const

{

   some_class obj;

   // ...

   return obj;

}

Если вам удастся вызвать конструктор копии в операторе return, то быть по сему.

Часть 8г. Конструкторы, деструкторы и operator=( )

Функции конструкторов, деструкторов и операций operator=()

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

Генерируемый компилятором конструктор копии (чьим аргументом является ссылка на текущий класс) нужен еще по двум причинам, кроме таблицы виртуальных функций. Во-первых, код на Си++, который выглядит как Си, должен и работать как Си. Так как правила копирования, которые относятся к классу, относятся также и к структуре, поэтому компилятор будет вынужден обычно генерировать конструктор копии в структуре, чтобы обрабатывать копирование структур в стиле Си. Этот конструктор копии используется явно подобным образом:


some_class x;             // конструктор по умолчанию
some_class y = x;      // конструктор копии
но кроме этого он используется и неявно в двух ситуациях. Первой является вызов по значению:
some_class x;
f( some_class x ) // передается по значению, а не по ссылке.
f( x );       // вызывается конструктор копии для передачи x
              // по значению. Оно должно скопироваться в стек.
Второй является возврат по значению:
some_class g() // Помните, что x
- локальная, автоматическая
               // переменная.Она исчезает после возвращения
               // функцией значения.
{
   some_class x; // Оператор return после этого должен
   return x;     // скопировать x куда-нибудь в надежное место
}                // (обычно в стек после аргументов).Он
                 // использует для этой цели конструктор копии.
Генерируемая компилятором функция-операция operator=()
нужна лишь для поддержки копирования структур в стиле Си там, где не определена операция присваивания.

Содержание раздела