|
Программирование >> Решение нетривиальных задач
public: derived( const derived &r ); const derived &operator=( const derived &r ); ----------------------------------------------------- derived::derived( const derived &r ) : base(r), s(r.s) -----------------------------------------------------const derived &derived:: operator=( const derived &r ) (* (base*) this) = r; s = r.s; Список инициализации членов в конструкторе копии описан ранее. Следующий отрывок из функции operator=() нуждается в некотором пояснении: (* (base*)this) = r; Указатель this указывает на весь текущий объект; добавление оператора приведения преобразует его в указатель на компонент базового класса в текущем объекте - (base*) this. (* (base*) this) является самим объектом, а выражение (* (base*) this) = r передает этому объекту сообщение, вызывая функцию operator=() базового класса для перезаписи информации из правого операнда в текущий объект. Вы могли бы заменить этот код таким образом: base:: operator=( r ); но я видел компиляторы, которые бракуют этот оператор, если в базовом классе не объявлена явно функция operator=(). Первая форма работает независимо от того, объявлена явно operator=(), или нет. (Если не объявлена, то у вас будет по умолчанию реализовано почленное копирование). Стандартом языка для этого предусмотрено ключевое слово explicit. - Ред. 132. Конструкторы, не предназначенные для преобразования типов, должны иметь два или более аргумента* Си++ использует конструкторы для преобразования типов. Например, конструктор char* в 9-ой строке листинга 7 на странице 155 также обрабатывает следующую операцию приведения: char *pchar = абвг ; (string) pchar; Запомните, что приведение является операцией времени выполнения, которая создает временную переменную нужного типа и инициализирует ее из аргумента. Если приводится класс, то для инициализации используется конструктор. Следующий код работает прекрасно, потому что строковая константа char* беспрепятственно преобразуется в string для передачи в функцию f() : f( const string &s ); ... f( белиберда ); Проблема состоит в том, что мы иногда не желаем использовать конструктор для неявного преобразования типов. Рассмотрим следующий контейнер массива, которым поддерживается целочисленный конструктор, определяющий размер этого массива: class array ... public: array( int initial size ); Вероятно вы все же не захотите, чтобы следующий код работал: f( const array &a ); ... f( isupper(*str) ); (Этот вызов передает f() пустой одноэлементный массив, если *str состоит из заглавных букв, или массив без элементов, если *str - из строчных букв). Единственным способом подавления такого поведения является добавление второго аргумента в конструктор, потому что конструкторы с несколькими аргументами никогда не используются неявно: class array ... public: enum bogus { set size to }; array( bogus, int initial size ); array ar( array::set size to, 128 ); Это по настоящему уродливо, но у нас нет выбора. Заметьте, что я не дал аргументу bogus имени, потому что он используется только для выбора функции. 133. Используйте счетчики экземпляров объектов для инициализации на уровне класса Несколько разделов назад я рассматривал использование счетчика статических глобальных объектов для управления инициализациями на уровне библиотеки. В Си++ у нас есть лучшие варианты, потому что мы может использовать определение класса для ограничения области действия: class window static int num windows; public: window(); ~window(); int window::num windows = 0; window::window() if( ++num windows == 1 ) только что создано первое окно initialize video system(); window::~window() if( --num windows == 0 ) только что уничтожено shut down video system(); последнее окно Наконец, счетчик экземпляров объектов может быть также использован в качестве счетчика числа вызовов для обеспечения инициализации на уровне подпрограммы:
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |