Программирование >>  Обобщенные обратные вызовы 

1 ... 55 56 57 [ 58 ] 59 60 61 ... 84


Задача 29. Инициализация ли это? Сложность: 3

в этой задаче мы рассмотрим такой вопрос: Что если мы инициализировали объект, и ничего не произошло?

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

Вопрос для новичка

1. Что делает следующий исходный текст?

deque<string> colli;

copyCistream iterator<string>( cin ),

i streanui terator<stri ng>0 ,

back inserter( colli ) );

Вопрос для профессионала

2. Что делает следующий исходный текст?

deque<string> со112( col 11.begiп(), colli.end() ); deque<string> coll3( istream iterator<string>( cin ), istream iterator<string>0 );

3. Что следует изменить в программе, чтобы она работала так, как, вероятно, ожидает программист?

Решение

Базовый механизм заполнения

1. Что делает следующий исходный текст?

пример 29 1(a)

deque<string> colli;

copy (istrearTL-i terator <string>( cin ),

istreanuiterator <stri ng>0 ,

back inserter( col11 ) );

В приведенном фрагменте объявляется изначально пустой дек строк с именем colli. Затем выполняется заполнение путе.м копирования строк, разделенных пробельными символами, из стандартного потока ввода cin в дек с использованием функции deque: :push back, пока не закончится весь входной поток.

Исходный текст примера 29-1(a) эквивалентен следующему.

пример 29-1(6): Эквивалентная запись 29-1(а)

deque<string> colli;

istream iterator<strinq> fi rst( cin ), last; copy( fi rst, last, bacK inserter( col 11 ) );

Единственное отличие от исходного текста 29-1(a) состоит в том, что в нем обьекты istream iterator создавались на лету , как временные неименованные, так что они уничтожались по завершении вызова сору. В примере 29-1(6) объекты i s-tream iterator являются именованными переменными и благополучно переживают вызов сору, они не будут уничтожены до тех пор, пока не будет достигнут конец области действия, в которой находится исходный текст примера 29-1(6).



Не инициализация

2. Что делает следующий исходный текст?

пример 29-2(а)

deque<string> соП2 ( colli, begin С), col 11. end () ) ;

В этом коде объявлен второй дек строк с именем соП2, и выполняется его заполнение с помощью подходящего конструктора. Здесь использован конструктор deque, который принимает пару итераторов, соответствующих диапазону, из которого должно выполняться копирование. В представленном исходном тексте col 12 инициализируется диапазоном итераторов, который соответствует всему содержимому colli .

Код примера 29-2(а) практически эквивалентен следующему.

пример 29-2(6): практически то же, что и в 29-2(а)

дополнительный шаг: вызов конструктора по умолчанию deque<string> coll2;

Добавление элементов с использованием push back

сору( colli.beginO , col ll.endO , back inserter( col 12 ) ) ;

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

Возможно, вы удивитесь, что я растолковываю очевидные вещи. Причина станет понятной, когда мы рассмотрим последнюю часть исходного текста. К сожалению, в нашем случае код ведет себя не так, как от него ожидают.

Пример 29-2(в): Объявление еще одного дека?

deque<string> со113( istream iterator <string>( cin ),

istream i terator <string>0 );

Код выглядит и ведет себя на первый взгляд так же, как и в примере 29-1(a), а именно - создает дек строк, который заполняется из стандартного потока ввода, с тем отличием, что он пытается использовать синтаксис из примера 29-2(а), а именно - воспользоваться конструктором с диапазоном итераторов, передаваемым при помощи параметров.

В этом исходном тексте есть одна потенциальная и одна реальная проблемы. Потенциальная проблема заключается в том, что стандартный ввод cin полностью поступает в контейнер, так что входные данные, необходимые для ввода в другой части программы, могут оказаться считанными в контейнер, что может вызвать определенные логические проблемы.

Однако самая большая проблема в том, что на самом деле приведенный исходный текст ничего этого не делает. Почему? Потому что это - не объявление объекта col 13 типа deque<string>. На самом деле это объявление (вдохните побольше воздуха) функции с именем col 13,

которая возвращает deque<stri ng> по значению

и получает два параметра:

istream iterator<string> с именем формального параметра cin, и функцию без имени формального параметра*,

Само собой разумеется, при этом происходит преобразование имени функции в указатель на нее. - Прим. ред.



которая не имеет параметров и возвращает istream i terator<string>. (Попробуйте-ка произнести это как скороговорку.)

Что же здесь происходит? Мы имеем дело с тяжким наследием С, правилом, которое оставлено для обеспечения обратной совместимости: если часть кода может быть интерпретирована как объявление, она и является объявлением. Процитируем стандарт С + +:

В грамматике имеется неоднозначность, когда инструкция может быть как выражением, так и объявлением. Если выражение с явным преобразованием типов в стиле вызова функции ( expr.type.conv ) является крайним слева, то оно может быть неотличимо от объявления, в котором первый оператор объявления начинается с открывающей круглой скобки ( . В этом случае инструкция рассматривается как объявление. - [С++03] §6.8

Не вдаваясь в детали, скажем, что причина возникновения этого правила - стремление помочь компиляторам при работе с жутким синтаксисом объявлений в С, который может оказаться неоднозначным. Чтобы компилятор мог справиться с этими неоднозначностями, введено универсальное положение - если ты в сомнении - это объявление .

Если вы еще не убеждены, то взгляните на задачу 42 из [SutterOO] (задача 10.1 в русском издании), где есть похожий, но более простой пример. Давайте разберем объявление щаг за шагом, чтобы понять, что оно означает.

Пример 29-2(г): Идентичен примеру 29-2(в), с удалением излишних скобок и добавлением typedef

typedef istream iterator<string> (Func)();

deque<stnng> соПЗ( istream i terator<string> cin, Func ) ;

Это более похоже на объявление функции? Может, да, может, нет - так что давайте сделаем следующий шаг и уберем имя формального параметра cin, которое все равно игнорируется, и изменим имя col 1 3 на нечто более похожее на обычное имя функции:

пример 29-2(д): идентичен примеру 29-2(в), с небольшими

изменениями имен

typedef istream iterator<string> (Func)(); deque<string> f( istream iterator<string>, Func );

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

Программисты время от времени сталкиваются с этой проблемой в своей работе, поэтому мы и отвели для ее рассмотрения отдельную задачу. Поскольку рассмотренный исходный текст, как это ни удивительно, представляет собой всего лишь объявление функции, реально он ничего не делает - для него компилятором не генерируется никакого кода, не выполняются никакие действия - не вызываются конструкторы дека, не создаются объекты.

Корректное заполнение

Было бы нечестно бросить вас на полпути, указав на проблему и не показав ее решения. А потому - последняя часть задачи:



1 ... 55 56 57 [ 58 ] 59 60 61 ... 84

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