Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 [ 59 ] 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76

списке инициализации членов, является несущественным. Более того, порядок объявления не должен рассматриваться как неизменный. Например, вы можете изменить порядок, в котором объявлены поля данных. Рассмотрим следующее определение класса где-нибудь в заголовочном файле:

class wilma

int y; int x; public:

wilma( int ix );

Вот определение конструктора в файле .с:

wilma::wilma( int ix ) : y(ix * 10), x(y + 1)

Теперь допустим, что какой-то сопровождающий программист переставит поля данных в алфавитном порядке, поменяв местами x и y. Этот конструктор больше не работает: поле x инициализируется первым, потому что оно первое в определении класса, и инициализируется значением y+1, но поле y еще не инициализировалось.

Исправьте код, исключив расчет на определенный порядок инициализации:

wilma::wilma( int ix ) : y(ix * 10), x((ix *10) + 1)

130. Конструкторы копий должны использовать списки инициализации членов

У наследования тоже есть свои проблемы с копированием. Конструктор копии все же остается конструктором, поэтому здесь также применимы результаты обсуждения предыдущего правила. Если у конструктора копии нет списка инициализации членов, то для базовых классов и вложенных объектов используется конструктор по умолчанию. Так как список инициализации членов отсутствует в следующем определении конструктора копии, то компонент базового класса в объекте производного класса инициализируется с использованием base( void) , а поле s инициализируется с использованием string::string( void) :

class base

public:

base( void );

base( const base &r );

конструктор по умолчанию конструктор копии



10 Книга Эллис и Страуструпа "The Annotated C++ Reference Manual" (Reading: Addison Wesley, 1990), использованная в качестве базового документа комитетом ISO/ANSI по Си++*.

*Имеется перевод на русский язык под редакцией А.Гутмана "Справочное руководство по языку программирования Си++ с комментариями" (М.: Мир, 1992). -Прим.перев.

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

class derived

string s; класс имеет конструктор копии

public:

derived( const derived &r )

derived::derived( const derived &r )

Чтобы гарантировать копирование также поля string и компонента базового класса в объекте производного класса, используйте следующее:

derived::derived( const derived &r ) : base(r), s(r.s) {}

131. Производные классы должны обычно определять конструктор копии и функцию operator=( )

При наследовании есть и другая связанная с копированием проблема. В одном месте руководства10 по языку Си++ недвусмысленно заявлено: "конструкторы и функция operator=() не наследуются". Однако далее в этом же документе говорится, что существуют ситуации, в которых компилятор не может создать конструктор копии или функцию operator=() , которые бы корректно вызывались вслед за функциями базового класса. Так как нет практической разницы между унаследованной и сгенерированной функциями operator=() , которые ничего не делают, кроме вызова функции базового класса, то эта неопределенность вызвала много бед.

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



11 Конечно, конструкторы копий и функции operator=(), создаваемые вами (в отличие от компилятора), никогда не вызывают своих двойников из базового класса автоматически.

объекта).11 Это как раз тот способ, который, по мнению большинства, реализуется языком программирования. Другими словами, со следующим кодом проблем не будет:

class base

public:

base( const base &r );

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

class derived : public base

string s;

нет операции operator=() или конструктора копии

derived x;

derivedy=x; вызывает конструктор копии базового класса для копирования базового класса. Также вызывает конструктор копии строки для копирования поля s. x=y; вызывает функцию базового класса operator=() для копирования базового класса. Также вызывает строковую функцию operator=() для копирования поля s.

Если бы все компиляторы работали таким образом, то проблемы бы не было. К несчастью, некоторые компиляторы принимают ту самую директиву "не наследуются" за чистую монету. Только что представленный код не будет работать с этими компиляторами. В них сгенерированные компилятором конструктор копии и функция operator=() производного класса действуют так, как будто бы их эквиваленты в базовом классе (и вложенном объекте) просто не существуют. Другими словами, конструктор по умолчанию - без аргументов - вызывается для копирования компонента базового класса, а почленное копирование - которое может выполняться просто функцией memcpy() - используется для поля. Мое понимание пересмотренного проекта стандарта Си++ ISO/ANSI позволяет сделать вывод, что такое поведение некорректно, но в течение некоторого времени вам придется рассчитывать на худшее, чтобы обеспечивать переносимость. Следовательно, это, вероятно, хорошая мысль - всегда помещать в производный класс конструктор копии и функцию operator=() , которые явно вызывают своих двойников из базового



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 [ 59 ] 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76