|
Программирование >> Решение нетривиальных задач
порядок объявления не должен рассматриваться как неизменный. Например, вы можете изменить порядок, в котором объявлены поля данных. Рассмотрим следующее определение класса где-нибудь в заголовочном файле: 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 ); конструктор копии 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 Это как раз тот способ, который, по мнению большинства, 10 Книга Эллис и Страуструпа The Annotated C++ Reference Manual (Reading: Addison Wesley, 1990), использованная в качестве базового документа комитетом 1SO/ANS1 по Си++*. *Имеется перевод на русский язык под редакцией А.Гутмана Справочное руководство по языку программирования Си++ с комментариями (М.: Мир, 1992). -Прим.перев. 11 Конечно, конструкторы копий и функции operator=(), создаваемые вами (в отличие от компилятора), никогда не вызывают своих двойников из базового класса автоматически. реализуется языком программирования. Другими словами, со следующим кодом проблем не будет: class base public: base( const base &r ); const base & operator=( const base &r ); class derived : public base string s; нет операции operator=() или конструктора копии derived x; derived y = x; вызывает конструктор копии базового класса для копирования базового класса. Также вызывает конструктор копии строки для копирования поля s. x = y; вызывает функцию базового класса operator=() для копирования базового класса. Также вызывает строковую функцию operator=() для копирования поля s. Если бы все компиляторы работали таким образом, то проблемы бы не было. К несчастью, некоторые компиляторы принимают ту самую директиву не наследуются за чистую монету. Только что представленный код не будет работать с этими компиляторами. В них сгенерированные компилятором конструктор копии и функция operator=() производного класса действуют так, как будто бы их эквиваленты в базовом классе (и вложенном объекте) просто не существуют. Другими словами, конструктор по умолчанию - без аргументов - вызывается для копирования компонента базового класса, а почленное копирование - которое может выполняться просто функцией memcpy() - используется для поля. Мое понимание пересмотренного проекта стандарта Си++ ISO/ANSI позволяет сделать вывод, что такое поведение некорректно, но в течение некоторого времени вам придется рассчитывать на худшее, чтобы обеспечивать переносимость. Следовательно, это, вероятно, хорошая мысль - всегда помещать в производный класс конструктор копии и функцию operator=() , которые явно вызывают своих двойников из базового класса. Вот реализация предыдущего производного класса для самого худшего случая: class derived : public base string s;
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |