Программирование >>  Решение нетривиальных задач 

1 ... 57 58 59 [ 60 ] 61 62 63 ... 77


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

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;



1 ... 57 58 59 [ 60 ] 61 62 63 ... 77

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика