
- •Предисловие
- •Структура книги
- •Благодарности
- •1. Начинаем
- •1.1. Решение задачи
- •1.2. Программа на языке C++
- •1.2.1. Порядок выполнения инструкций
- •1.3. Директивы препроцессора
- •1.4. Немного о комментариях
- •1.5. Первый взгляд на ввод/вывод
- •1.5.1. Файловый ввод/вывод
- •2. Краткий обзор С++
- •2.1. Встроенный тип данных “массив”
- •2.2. Динамическое выделение памяти и указатели
- •2.3. Объектный подход
- •2.4. Объектно-ориентированный подход
- •2.5. Использование шаблонов
- •2.7. Использование пространства имен
- •2.8. Стандартный массив – это вектор
- •Часть II
- •3. Типы данных С++
- •3.1. Литералы
- •3.2. Переменные
- •3.2.1. Что такое переменная
- •3.2.2. Имя переменной
- •3.2.3. Определение объекта
- •3.3. Указатели
- •3.4. Строковые типы
- •3.4.1. Встроенный строковый тип
- •3.4.2. Класс string
- •3.5. Спецификатор const
- •3.6. Ссылочный тип
- •3.7. Тип bool
- •3.8. Перечисления
- •3.9. Тип “массив”
- •3.9.1. Многомерные массивы
- •3.9.2. Взаимосвязь массивов и указателей
- •3.10. Класс vector
- •3.11. Класс complex
- •3.12. Директива typedef
- •3.14. Класс pair
- •3.15. Типы классов
- •4. Выражения
- •4.2. Арифметические операции
- •4.3. Операции сравнения и логические операции
- •4.4. Операции присваивания
- •4.5. Операции инкремента и декремента
- •4.6. Операции с комплексными числами
- •4.7. Условное выражение
- •4.8. Оператор sizeof
- •4.9. Операторы new и delete
- •4.10. Оператор “запятая”
- •4.11. Побитовые операторы
- •4.12. Класс bitset
- •4.13. Приоритеты
- •4.14. Преобразования типов
- •4.1. Что такое выражение?
- •4.14.1. Неявное преобразование типов
- •4.14.2. Арифметические преобразования типов
- •4.14.3. Явное преобразование типов
- •4.14.4. Устаревшая форма явного преобразования
- •4.15. Пример: реализация класса Stack
- •5. Инструкции
- •5.1. Простые и составные инструкции
- •5.2. Инструкции объявления
- •5.3. Инструкция if
- •5.4. Инструкция switch
- •5.5. Инструкция цикла for
- •5.6. Инструкция while
- •5.8. Инструкция do while
- •5.8. Инструкция break
- •5.9. Инструкция continue
- •5.10. Инструкция goto
- •5.11. Пример связанного списка
- •5.11.1. Обобщенный список
- •6. Абстрактные контейнерные типы
- •6.1. Система текстового поиска
- •6.2. Вектор или список?
- •6.3. Как растет вектор?
- •6.4. Как определить последовательный контейнер?
- •6.5. Итераторы
- •6.6. Операции с последовательными контейнерами
- •6.6.1. Удаление
- •6.6.2. Присваивание и обмен
- •6.6.3. Обобщенные алгоритмы
- •6.7. Читаем текстовый файл
- •6.8. Выделяем слова в строке
- •6.9. Обрабатываем знаки препинания
- •6.10. Приводим слова к стандартной форме
- •6.11. Дополнительные операции со строками
- •6.12. Строим отображение позиций слов
- •6.12.2. Поиск и извлечение элемента отображения
- •6.12.3. Навигация по элементам отображения
- •6.12.4. Словарь
- •6.12.5. Удаление элементов map
- •6.13. Построение набора стоп-слов
- •6.13.2. Поиск элемента
- •6.13.3. Навигация по множеству
- •6.14. Окончательная программа
- •6.15. Контейнеры multimap и multiset
- •6.16. Стек
- •6.17. Очередь и очередь с приоритетами
- •6.18. Вернемся в классу iStack
- •Часть III
- •7. Функции
- •7.1. Введение
- •7.2. Прототип функции
- •7.2.1. Тип возвращаемого функцией значения
- •7.2.2. Список параметров функции
- •7.2.3. Проверка типов формальных параметров
- •7.3. Передача аргументов
- •7.3.1. Параметры-ссылки
- •7.3.2. Параметры-ссылки и параметры-указатели
- •7.3.3. Параметры-массивы
- •7.3.5. Значения параметров по умолчанию
- •7.3.6. Многоточие
- •7.4. Возврат значения
- •7.5. Рекурсия
- •7.6. Встроенные функции
- •7.7. Директива связывания extern "C" A
- •7.8.1. Класс для обработки параметров командной строки
- •7.9. Указатели на функции
- •7.9.1. Тип указателя на функцию
- •7.9.2. Инициализация и присваивание
- •7.9.3. Вызов
- •7.9.4. Массивы указателей на функции
- •7.9.5. Параметры и тип возврата
- •7.9.6. Указатели на функции, объявленные как extern "C"
- •8. Область видимости и время жизни
- •8.1. Область видимости
- •8.1.1. Локальная область видимости
- •8.2. Глобальные объекты и функции
- •8.2.1. Объявления и определения
- •8.2.2. Сопоставление объявлений в разных файлах
- •8.2.3. Несколько слов о заголовочных файлах
- •8.3. Локальные объекты
- •8.3.1. Автоматические объекты
- •8.3.2. Регистровые автоматические объекты
- •8.3.3. Статические локальные объекты
- •8.4. Динамически размещаемые объекты
- •8.4.2. Шаблон auto_ptr А
- •8.4.3. Динамическое создание и уничтожение массивов
- •8.4.5. Оператор размещения new А
- •8.5. Определения пространства имен А
- •8.5.1. Определения пространства имен
- •8.5.2. Оператор разрешения области видимости
- •8.5.3. Вложенные пространства имен
- •8.5.4. Определение члена пространства имен
- •8.5.5. ПОО и члены пространства имен
- •8.5.6. Безымянные пространства имен
- •8.6. Использование членов пространства имен А
- •8.6.1. Псевдонимы пространства имен
- •8.6.2. Using-объявления
- •8.6.3. Using-директивы
- •8.6.4. Стандартное пространство имен std
- •9. Перегруженные функции
- •9.1. Объявления перегруженных функций
- •9.1.1. Зачем нужно перегружать имя функции
- •9.1.2. Как перегрузить имя функции
- •9.1.3. Когда не надо перегружать имя функции
- •9.1.4. Перегрузка и область видимости A
- •9.1.5. Директива extern "C" и перегруженные функции A
- •9.1.6. Указатели на перегруженные функции A
- •9.1.7. Безопасное связывание A
- •9.2. Три шага разрешения перегрузки
- •9.3. Преобразования типов аргументов A
- •9.3.1. Подробнее о точном соответствии
- •9.3.3. Подробнее о стандартном преобразовании
- •9.3.4. Ссылки
- •9.4. Детали разрешения перегрузки функций
- •9.4.1. Функции-кандидаты
- •9.4.2. Устоявшие функции
- •9.4.3. Наилучшая из устоявших функция
- •9.4.4. Аргументы со значениями по умолчанию
- •10. Шаблоны функций
- •10.1. Определение шаблона функции
- •10.2. Конкретизация шаблона функции
- •10.3. Вывод аргументов шаблона А
- •10.4. Явное задание аргументов шаблона A
- •10.5. Модели компиляции шаблонов А
- •10.5.1. Модель компиляции с включением
- •10.5.2. Модель компиляции с разделением
- •10.5.3. Явные объявления конкретизации
- •10.6. Явная специализация шаблона А
- •10.7. Перегрузка шаблонов функций А
- •10.8. Разрешение перегрузки при конкретизации A
- •10.9. Разрешение имен в определениях шаблонов А
- •10.10. Пространства имен и шаблоны функций А
- •10.11. Пример шаблона функции
- •11. Обработка исключений
- •11.1. Возбуждение исключения
- •11.2. try-блок
- •11.3. Перехват исключений
- •11.3.1. Объекты-исключения
- •11.3.2. Раскрутка стека
- •11.3.3. Повторное возбуждение исключения
- •11.3.4. Перехват всех исключений
- •11.4. Спецификации исключений
- •11.4.1. Спецификации исключений и указатели на функции
- •11.5. Исключения и вопросы проектирования
- •12. Обобщенные алгоритмы
- •12.1. Краткий обзор
- •12.2. Использование обобщенных алгоритмов
- •12.3. Объекты-функции
- •12.3.1. Предопределенные объекты-функции
- •12.3.2. Арифметические объекты-функции
- •12.3.3. Сравнительные объекты-функции
- •12.3.4. Логические объекты-функции
- •12.3.5. Адаптеры функций для объектов-функций
- •12.3.6. Реализация объекта-функции
- •12.4. Еще раз об итераторах
- •12.4.1. Итераторы вставки
- •12.4.2. Обратные итераторы
- •12.4.3. Потоковые итераторы
- •12.4.4. Итератор istream_iterator
- •12.4.5. Итератор ostream_iterator
- •12.4.6. Пять категорий итераторов
- •12.5. Обобщенные алгоритмы
- •12.5.1. Алгоритмы поиска
- •12.5.2. Алгоритмы сортировки и упорядочения
- •12.5.3. Алгоритмы удаления и подстановки
- •12.5.4. Алгоритмы перестановки
- •12.5.5. Численные алгоритмы
- •12.5.6. Алгоритмы генерирования и модификации
- •12.5.7. Алгоритмы сравнения
- •12.5.8. Алгоритмы работы с множествами
- •12.5.9. Алгоритмы работы с хипом
- •12.6.1. Операция list_merge()
- •12.6.2. Операция list::remove()
- •12.6.3. Операция list::remove_if()
- •12.6.4. Операция list::reverse()
- •12.6.5. Операция list::sort()
- •12.6.6. Операция list::splice()
- •12.6.7. Операция list::unique()
- •Часть IV
- •13. Классы
- •13.1. Определение класса
- •13.1.1. Данные-члены
- •13.1.2. Функции-члены
- •13.1.3. Доступ к членам
- •13.1.4. Друзья
- •13.1.5. Объявление и определение класса
- •13.2. Объекты классов
- •13.3. Функции-члены класса
- •13.3.1. Когда использовать встроенные функции-члены
- •13.3.2. Доступ к членам класса
- •13.3.3. Закрытые и открытые функции-члены
- •13.3.4. Специальные функции-члены
- •13.3.5. Функции-члены со спецификаторами const и volatile
- •13.3.6. Объявление mutable
- •13.4. Неявный указатель this
- •13.4.1. Когда использовать указатель this
- •13.5. Статические члены класса
- •13.5.1. Статические функции-члены
- •13.6. Указатель на член класса
- •13.6.1. Тип члена класса
- •13.6.2. Работа с указателями на члены класса
- •13.6.3. Указатели на статические члены класса
- •13.7. Объединение – класс, экономящий память
- •13.8. Битовое поле – член, экономящий память
- •13.9. Область видимости класса A
- •13.9.1. Разрешение имен в области видимости класса
- •13.10. Вложенные классы A
- •13.11. Классы как члены пространства имен A
- •13.12. Локальные классы A
- •14.1. Инициализация класса
- •14.2. Конструктор класса
- •14.2.1. Конструктор по умолчанию
- •14.2.2. Ограничение прав на создание объекта
- •14.2.3. Копирующий конструктор
- •14.3. Деструктор класса
- •14.3.1. Явный вызов деструктора
- •14.3.2. Опасность увеличения размера программы
- •14.4. Массивы и векторы объектов
- •14.4.1. Инициализация массива, распределенного из хипа A
- •14.4.2. Вектор объектов
- •14.5. Список инициализации членов
- •14.6. Почленная инициализация A
- •14.6.1. Инициализация члена, являющегося объектом класса
- •14.7. Почленное присваивание A
- •14.8. Соображения эффективности A
- •15.1. Перегрузка операторов
- •15.1.1. Члены и не члены класса
- •15.1.2. Имена перегруженных операторов
- •15.1.3. Разработка перегруженных операторов
- •15.2. Друзья
- •15.3. Оператор =
- •15.4. Оператор взятия индекса
- •15.5. Оператор вызова функции
- •15.6. Оператор “стрелка”
- •15.7. Операторы инкремента и декремента
- •15.8. Операторы new и delete
- •15.8.1. Операторы new[ ] и delete [ ]
- •15.8.2. Оператор размещения new() и оператор delete()
- •15.9. Определенные пользователем преобразования
- •15.9.1. Конвертеры
- •15.9.2. Конструктор как конвертер
- •15.10. Выбор преобразования A
- •15.10.1. Еще раз о разрешении перегрузки функций
- •15.10.2. Функции-кандидаты
- •15.11. Разрешение перегрузки и функции-члены A
- •15.11.1. Объявления перегруженных функций-членов
- •15.11.2. Функции-кандидаты
- •15.11.3. Устоявшие функции
- •15.12. Разрешение перегрузки и операторы A
- •15.12.1. Операторные функции-кандидаты
- •15.12.2. Устоявшие функции
- •15.12.3. Неоднозначность
- •16. Шаблоны классов
- •16.1. Определение шаблона класса
- •16.1.1. Определения шаблонов классов Queue и QueueItem
- •16.2. Конкретизация шаблона класса
- •16.2.1. Аргументы шаблона для параметров-констант
- •16.3. Функции-члены шаблонов классов
- •16.3.1. Функции-члены шаблонов Queue и QueueItem
- •16.4. Объявления друзей в шаблонах классов
- •16.4.1. Объявления друзей в шаблонах Queue и QueueItem
- •16.5. Статические члены шаблонов класса
- •16.6. Вложенные типы шаблонов классов
- •16.7. Шаблоны-члены
- •16.8. Шаблоны классов и модель компиляции A
- •16.8.1. Модель компиляции с включением
- •16.8.2. Модель компиляции с разделением
- •16.8.3. Явные объявления конкретизации
- •16.9. Специализации шаблонов классов A
- •16.10. Частичные специализации шаблонов классов A
- •16.11. Разрешение имен в шаблонах классов A
- •16.12. Пространства имен и шаблоны классов
- •16.13. Шаблон класса Array
- •Часть V
- •17. Наследование и подтипизация классов
- •17.1. Определение иерархии классов
- •17.1.1. Объектно-ориентированное проектирование
- •17.2. Идентификация членов иерархии
- •17.2.1. Определение базового класса
- •17.2.2. Определение производных классов
- •17.2.3. Резюме
- •17.3. Доступ к членам базового класса
- •17.4. Конструирование базового и производного классов
- •17.4.1. Конструктор базового класса
- •17.4.2. Конструктор производного класса
- •17.4.3. Альтернативная иерархия классов
- •17.4.4. Отложенное обнаружение ошибок
- •17.4.5. Деструкторы
- •17.5.1. Виртуальный ввод/вывод
- •17.5.2. Чисто виртуальные функции
- •17.5.3. Статический вызов виртуальной функции
- •17.5.4. Виртуальные функции и аргументы по умолчанию
- •17.5.5. Виртуальные деструкторы
- •17.5.6. Виртуальная функция eval()
- •17.5.7. Почти виртуальный оператор new
- •17.5.8. Виртуальные функции, конструкторы и деструкторы
- •17.6. Почленная инициализация и присваивание A
- •17.7. Управляющий класс UserQuery
- •17.7.1. Определение класса UserQuery
- •17.8. Соберем все вместе
- •18.1. Готовим сцену
- •18.2. Множественное наследование
- •18.3. Открытое, закрытое и защищенное наследование
- •18.3.1. Наследование и композиция
- •18.3.2. Открытие отдельных членов
- •18.3.3. Защищенное наследование
- •18.3.4. Композиция объектов
- •18.4. Область видимости класса и наследование
- •18.5. Виртуальное наследование A
- •18.5.1. Объявление виртуального базового класса
- •18.5.2. Специальная семантика инициализации
- •18.5.3. Порядок вызова конструкторов и деструкторов
- •18.5.4. Видимость членов виртуального базового класса
- •18.6.2. Порождение класса отсортированного массива
- •18.6.3. Класс массива с множественным наследованием
- •19. Применение наследования в C++
- •19.1. Идентификация типов во время выполнения
- •19.1.1. Оператор dynamic_cast
- •19.1.2. Оператор typeid
- •19.1.3. Класс type_info
- •19.2. Исключения и наследование
- •19.2.1. Исключения, определенные как иерархии классов
- •19.2.2. Возбуждение исключения типа класса
- •19.2.3. Обработка исключения типа класса
- •19.2.4. Объекты-исключения и виртуальные функции
- •19.2.5. Раскрутка стека и вызов деструкторов
- •19.2.6. Спецификации исключений
- •19.2.7. Конструкторы и функциональные try-блоки
- •19.3. Разрешение перегрузки и наследование A
- •19.3.1. Функции-кандидаты
- •19.3.3. Наилучшая из устоявших функций
- •20. Библиотека iostream
- •20.1. Оператор вывода <<
- •20.2. Ввод
- •20.2.1. Строковый ввод
- •20.3. Дополнительные операторы ввода/вывода
- •20.4. Перегрузка оператора вывода
- •20.5. Перегрузка оператора ввода
- •20.6. Файловый ввод/вывод
- •20.7. Состояния потока
- •20.8. Строковые потоки
- •20.9. Состояние формата
- •20.10. Сильно типизированная библиотека
- •accumulate()
- •adjacent_difference()
- •adjacent_find()
- •binary_search()
- •copy()
- •copy_backward()
- •count_if()
- •equal()
- •equal_range()
- •fill()
- •find()
- •find_if()
- •find_end()
- •find_first_of()
- •generate()
- •generate_n()
- •includes()
- •inplace_merge()
- •iter_swap()
- •lexicographical_compare()
- •max_element()
- •merge()
- •next_permutation()
- •nth_element()
- •partial_sort()
- •partial_sort_copy()
- •partition()
- •prev_permutation()
- •random_shuffle()
- •remove()
- •remove_if()
- •remove_copy_if()
- •replace_copy()
- •replace_if()
- •replace_copy_if()
- •reverse_copy()
- •rotate()
- •search_n()
- •set_difference()
- •set_intersection()
- •set_union()
- •sort()
- •stable_partition()
- •swap()
- •swap_ranges()
- •transform()
- •unique_copy()
- •upper_bound()
- •Алгоритмы для работы с хипом
- •make_heap()
- •pop_heap()
- •push_heap()
- •sort_heap()
С++ для начинающих |
370 |
8.Область видимости и время жизни
Вэтой главе обсуждаются два важных вопроса, касающиеся объявлений в С++. Где употребляется объявленное имя? Когда можно безопасно использовать объект или вызывать функцию, т.е. каково время жизни сущности в программе? Для ответа на первый вопрос мы введем понятие областей видимости и покажем, как они ограничивают применение имен в исходном файле программы. Мы рассмотрим разные типы таких областей: глобальную и локальную, а также более сложное понятие областей видимости пространств имен, которое появится в конце главы. Отвечая на второй вопрос, мы опишем, как объявления вводят глобальные объекты и функции (сущности, “живущие” в течение всего времени работы программы), локальные (“живущие” на определенном отрезке выполнения) и динамически размещаемые объекты (временем жизни которых управляет программист). Мы также исследуем свойства времени выполнения, характерные для этих объектов и функций.
8.1.Область видимости
Каждое имя в С++ программе должно относиться к уникальной сущности (объекту, функции, типу или шаблону). Это не значит, что оно встречается только один раз во всей программе: его можно повторно использовать для обозначения другой сущности, если только есть некоторый контекст, помогающий различить разные значения одного и того же имени. Контекстом, служащим для такого различения, служит область видимости. В С++ поддерживается три их типа: локальная область видимости, область видимости
пространства имен и область видимости класса.
Локальная область – это часть определении функции (или в блоке). каждая составная инструкция (или отдельную локальную область.
исходного текста программы, содержащаяся в Любая функция имеет собственную такую часть, и блок) внутри функции также представляет собой
Область видимости пространства имен – часть исходного текста программы, не содержащаяся внутри объявления или определения функции или определения класса.
Самая внешняя часть называется глобальной областью видимости или глобальной областью видимости пространства имен.
Объекты, функции, типы и шаблоны могут быть определены в глобальной области видимости. Программисту разрешено задать пользовательские пространства имен, заключенные внутри глобальной области с помощью определения пространства имен. Каждое такое пространство является отдельной областью видимости. Пользовательское пространство, как и глобальное, может содержать объявления и определения объектов, функций, типов и шаблонов, а также вложенные пользовательские пространства имен. (Они рассматриваются в разделах 8.5 и 8.6.)
Каждое определение класса представляет собой отдельную область видимости класса. (О таких областях мы расскажем в главе 13.)
Имя может обозначать различные сущности в зависимости от области видимости. В следующем фрагменте программы имя s1 относится к четырем разным сущностям:

С++ для начинающих |
371 |
||
|
|
#include <iostream> |
|
|
|
|
|
|
|
#include <string> |
|
|
|
// сравниваем s1 и s2 лексикографически |
|
|
|
int lexicoCompare( const string &sl, const string &s2 ) { ... } |
|
|
|
// сравниваем длины s1 и s2 |
|
|
|
int sizeCompare( const string &sl, const string &s2 ) { ... } |
|
|
|
typedef int ( PFI)( const string &, const string & ); |
|
|
|
// сортируем массив строк |
|
|
|
void sort( string *s1, string *s2, PFI compare =lexicoCompare ) |
|
|
|
{ ... } |
|
|
|
string sl[10] = { "a", "light", "drizzle", "was", "falling", |
|
|
|
"when", "they", "left", "the", "school" }; |
|
|
|
int main() |
|
|
|
{ |
|
|
|
// вызов sort() со значением по умолчанию параметра compare |
|
|
|
// s1 - глобальный массив |
|
|
|
sort( s1, s1 + sizeof(s1)/sizeof(s1[0]) - 1 ); |
|
|
|
// выводим результат сортировки |
|
|
|
for ( int i = 0; i < sizeof(s1) / sizeof(s1[0]); ++i ) |
|
|
|
cout << s1[ i ].c_str() << "\n\t"; |
|
|
|
} |
|
|
|
||
|
|
|
|
Поскольку определения функций lexicoCompare(), sizeCompare() и sort()
представляют собой различные области видимости и все они отличны от глобальной, в каждой из этих областей можно завести переменную с именем s1.
Имя, введенное с помощью объявления, можно использовать от точки объявления до конца области видимости (включая вложенные области). Так, имя s1 параметра функции lexicoCompare() разрешается употреблять до конца ее области видимости, то есть до конца ее определения.
Имя глобального массива s1 видимо с точки его объявления до конца исходного файла, включая вложенные области, такие, как определение функции main().
В общем случае имя должно обозначать одну сущность внутри одной области видимости. Если в предыдущем примере после объявления массива s1 добавить следующую строку, компилятор выдаст сообщение об ошибке:
void s1(); // ошибка: повторное объявление s1
Перегруженные функции являются исключением из правила: можно завести несколько одноименных функций в одной области видимости, если они отличаются списком параметров. (Перегруженные функции рассматриваются в главе 9.)
ВС++ имя должно быть объявлено до момента его первого использования в выражении.
Впротивном случае компилятор выдаст сообщение об ошибке. Процесс сопоставления имени, используемого в выражении, с его объявлением называется разрешением. С помощью этого процесса имя получает конкретный смысл. Разрешение имени зависит от способа его употребления и от его области видимости. Мы рассмотрим этот процесс в различных контекстах. (В следующем подразделе описывается разрешение имен в
локальной области видимости; в разделе 10.9 – разрешение в шаблонах функций; в конце главы 13 – в области видимости классов, а в разделе 16.12 – в шаблонах классов.)

С++ для начинающих |
372 |
Области видимости и разрешение имен – понятия времени компиляции. Они применимы к отдельным частям текста программы. Компилятор интерпретирует текст программы согласно правилам областей видимости и правилам разрешения имен.
8.1.1. Локальная область видимости
Локальная область видимости – это часть исходного текста программы, содержащаяся в определении функции (или блоке внутри тела функции). Все функции имеют свои локальные области видимости. Каждая составная инструкция (или блок) внутри функции также представляет собой отдельную локальную область. Такие области могут быть вложенными. Например, следующее определение функции содержит два их уровня
const int notFound = -1; // глобальная область видимости int binSearch( const vector<int> &vec, int val )
{ // локальная область видимости: уровень #1 int low = 0;
int high = vec.size() - 1;
while ( low <= high )
{ // локальная область видимости: уровень #2 int mid = ( low + high ) / 2;
if ( val < vec[ mid ] ) high = mid - 1;
else low = mid + 1;
}
return notFound; // локальная область видимости: уровень #1
(функция выполняет двоичный поиск в отсортированном векторе целых чисел):
}
Первая локальная область видимости – тело функции binSearch(). В ней объявлены параметры функции vec и val, а также переменные low и high. Цикл while внутри функции задает вложенную локальную область, в которой определена одна переменная mid. Параметры vec и val и переменные low и high видны во вложенной области. Глобальная область видимости включает в себя обе локальных. В ней определена одна целая константа notFound.
Имена параметров функции vec и val принадлежат к первой локальной области видимости тела функции, и в ней использовать те же имена для других сущностей нельзя.
int binSearch( const vector<int> &vec, int val ) { // локальная область видимости: уровень #1
int val; // ошибка: неверное переопределение val
Например:
// ...
Имена параметров употребляются как внутри тела функции binSearch(), так и внутри вложенной области видимости цикла while. Параметры vec и val недоступны вне тела функции binSearch().
Разрешение имени в локальной области видимости происходит следующим образом: просматривается та область, где оно встретилось. Если объявление найдено, имя разрешено. Если нет, просматривается область видимости, включающая текущую. Этот

С++ для начинающих |
373 |
процесс продолжается до тех пор, пока объявление не будет найдено либо не будет достигнута глобальная область видимости. Если и там имени нет, оно будет считаться ошибочным.
Из-за порядка просмотра областей видимости в процессе разрешения имен объявление из внешней области может быть скрыто объявлением того же имени во вложенной области.
Если бы в предыдущем примере переменная low была объявлена в глобальной области видимости перед определением функции binSearch(), то использование low в
локальной области видимости цикла while все равно относилось бы к локальному
int low;
int binSearch( const vector<int> &vec, int val )
{
//локальное объявление low
//скрывает глобальное объявление int low = 0;
//...
//low - локальная переменная while ( low <= high )
{//...
}
// ...
объявлению, скрывающему глобальное:
}
Для некоторых инструкций языка C++ разрешено объявлять переменные внутри управляющей части. Например, в цикле for переменную можно определить внутри
for ( int index = 0; index < vecSize; ++index )
{
// переменная index видна только здесь if ( vec[ index ] == someValue )
break;
}
// ошибка: переменная index не видна
инструкции инициализации:
if ( index != vecSize ) // элемент найден
Подобные переменные видны только в локальной области самого цикла for и вложенных в него (это верно для стандарта С++, в предыдущих версиях языка поведение было иным). Компилятор рассматривает это объявление так же, как если бы оно было записано
// представление компилятора
{// невидимый блок int index = 0;
for ( ; index < vecSize; ++index )
{
// ...
}
в виде:
}

С++ для начинающих |
374 |
Тем самым программисту запрещается применять управляющую переменную вне локальной области видимости цикла. Если нужно проверить index, чтобы определить,
int index = 0;
for ( ; index < vecSize; ++index )
{
// ...
}
// правильно: переменная index видна
было ли найдено значение, то данный фрагмент кода следует переписать так: if ( index != vecSize ) // элемент найден
Поскольку переменная, объявленная в инструкции инициализации цикла for, является локальной для цикла, то же самое имя допустимо использовать аналогичным образом и в
void fooBar( int *ia, int sz )
{
for (int i=0; i<sz; ++i) ... // правильно
for (int i=0; i<sz; ++i) ... // правильно, другое i for (int i=0; i<sz; ++i) ... // правильно, другое i
других циклах, расположенных в данной локальной области видимости:
}
Аналогично переменная может быть объявлена внутри условия инструкций if и switch,
if ( int *pi = getValue() )
{
//pi != 0 -- *pi можно использовать здесь int result = calc(*pi);
//...
}
else
{
//здесь pi тоже видна
//pi == 0
cout << "ошибка: getValue() завершилась неудачно" << endl;
а также внутри условия циклов while и for. Например:
}
Переменные, определенные в условии инструкции if, как переменная pi, видны только внутри if и соответствующей части else, а также во вложенных областях. Значением условия является значение этой переменной, которое она получает в результате инициализации. Если pi равна 0 (нулевой указатель), условие ложно и выполняется ветвь else. Если pi инициализируется любым другим значением, условие истинно и выполняется ветвь if. (Инструкции if, switch, for и while рассматривались в главе 5.)
Упражнение 8.1
Найдите различные области видимости в следующем примере. Какие объявления ошибочны и почему?