Г. Шилдт - Полный справочник по C++ (1109478), страница 54
Текст из файла (страница 54)
Деструкторы локальных объектов вызываются в обратном порядке. Конструкторы глобальных обьектов выполняются до функции ве)п(). Глобальные конструкторы выполняются в порядке их объявления в файле. Порядок вызова глобальных конструкторов, распределенных в нескольких файлах, заранее не определен. Глобальные деструкторы вызываются в обратном порядке восле завершения работы Функции зва4п(). Вьповы конструкторов и деструкторов иллюстрируются следуюшей программой. ()гпс1ибе <1оягсеав> ив1по павеврасе зМ; с1аяв жус1авв ( риЬ11с: ЫС ино; тус1аяв(ЫС Ы); -жус1авя(); д1оЬ оЬ1(1), д1оЬ оЬ2 (2) з тус1азв: паус1авв [хпс Ы) соис « "инициализация " « Ы « "1п"; ино = Ы> тпус1азв::-жус1азя() ( соие « 'Уничтожение " «ибо « "1п"; гпс таъп() ( пус1аяз 1оса1 оЬ1(3) соие « "Эта строка уже не будет первой.хп"; вус1аяв 1оса1 оЬ2(4); гесисп О," В результате на экране появятся следуюшие строки.
Инициализация 1 Инициализация 2 Инициализация 3 Эта строка Уже не будет первой. Инициализация 4 Уничтожение 4 Уничтожение 3 Уничтожение 2 Уничтожение 1 И еше: поскольку компиляторы и операционные системы отличаются друг от друга, послелние две строки не всегда появляются на экране. Глава 12. Классы и объекты Оператор разрешения области видимости Как известна, оператор '"т т" связывает межлу собой имена класса и его члена, сообшав компилятору, какому классу принадлежит данный член, Однако оператор разрешения области видимости имеет еше одно прелназначение: он открывает поступ к переменной, имя которой "замаскирована" внупзи вложенной области видимости. В качестве примера рассмотрим следуюший фрагмент программы. юпе вт // Глобальная переменная уоас( Г() ( 1пс 1/ // локальная переменная 1 10/ // Используем локальную переменную 1 Как указано в комментарии, присваивание 1=10 относится к локальной переменной в.
А что делать, если функции йО нужен доступ к глобальной переменной 1? Тогда следует применить оператор разрешения области видимости. 1пе Гт // Глобальная переменная 1. уоЫ с() ( Гпс 1; // Локальная переменная 1. — 10; // Обрапенне к глобальной переменной 1. ~:1 Вложенные классы Один класс можно определить внутри другого. Таким образом создаются вложенные классы. Поскольку объявление класса фактически определяет его область видимости, вложенные классы могут сушествовать только внутри области вилимости внешнега класса. Вообше-то, вложенные классы используются редко. Поскольку в языке С++ сушествует гибкий и мошный механизм наследования, необходимости создавать вложенные классы практически нет. ~ ~ Локальные классы Класс можно определить внутри функции.
Рассмотрим следуюший пример. | Зьпс1пбе <йозегеаю> азгпу патезрасе зсс(т Часть П. Язык С++ тпс ва1п() ( т(); // Класс вус1аяя отсюда не виден, тесцтп ог ) чоЫ т() ( с1аяя вус1аяя 1гс 1; рцЬ) тс: чоЫ рцс 1(Ыс г) ( т=п; ) тпс пес 1() ( тесцтп 1: ) оЬ; оЬ.рос соце <с оЬ.Пее 1 О," На локальные классы налагается несколько ограничений. Во-первых, все функции-члены должны определяться внутри объявления локального класса. Вовторых, локальные классы не могут обрашаться к локальным переменным, объявленным вн)чри функции, за исключением с~а~ических локальных переменных (аеае1с) и внешних переменных (еиеетп).
Однако они имеют доступ к именам типов и перечислениям, опрелеленным во внешней функции. В-третьих, внутри локальных классов нельзя объявлять с~этические переменные Из-за этих ограничений локальные классы применяются довольно редко. ' ~ Передача объектов функциям Объекты можно передавать функциям, как обычные переменные. Для этого применяется обычный механизм передачи параметров по значению. Несмотря на внешнюю простоту, этот процесс может привести к неожиданным последствиям, касаюшимся конструкторов и деструкторов. Попробуем разобраться в этом с помошью следуюшей программы.
// Передача объекта Функции. Ятпс1иг)е <тояттеать цятпп павеярасе ясс(; с1аяя вус1аяя 1пс 1; рцЬ11с: вус1аяя(тпе и); -вус)аяя(); уоЫ яес т(1пе и) ( 1=п; 1гс Пее 1() ( тесцтп 1; )' вус1аяя::вус1аяя(тпс и) ъ=п; Глава (2. Классы и объекты соне « "Создание " « 1 « "хп"; ь пус1аяя::-тус1аяя() ( сопс « "Уничтожение " «1 « "1п" ь ) тя уойс( й(тьус1аяя оЬ)' й йпс тайп () ( пус1аяя о(1); й(о); сопс « "Это переменная й в функции тайп: соне « о.яее й() « " 1п"; сесссп О; ) Уо1б й(тус1аяя оЬ) ( оЬ.яес 1(2); сопс « "это локальная переменная 1: " « оь.пес 1 О сопс « "1п" ь В результате работы этой программы на экране появятся следуюшие строки. Создание 1 Это локальная переменная й: 2 Уничтожение 2 Это переменная й в функции пьзйп: 1 Уничтожение 1 Как показывают эти результаты, в ходе работы программы конструктор вызывался лишь однажды, при создании объекта о в функпии пайп(), в то же время, деструктор был вызван дважды.
Рассмотрим причины этой сигурни. Когда объект передается в функпию, создается его копия. Именно эта копия становится параметром функпии. Это означает, что в программе появился новый объект. По завершении работы функпии копия аргумента (т.е, параметр) уничтожается.
Это порождает лва принпипиально важных вопроса. Во-первых, вызывается ли конструктор при созлании копии аргумента? Во-вторых, вызывается ли деструктор при уничтожении параметра? Ответы на эти вопросы вас удивят. При создании копии аргумента обычный конструктор не яызыяаегяся. Вместо него вызывается конструктор кояирояаная. Именно он и создает новую копию аргумента. Как указывается в главе!4, конструктор копирования можно опрелелять явно. Однако, если объявление класса не содержит явного определения конструктора копирования, как в нашем примере, вызывается автоматический конструктор копирования„предусмотренный по умолчанию. Он создает побитовую (т.е.
идентичную) копию объекта. Это легко понять. Ведь обычный конструктор нельзя вызывать для создания копии уже сушествуюшего объекта, поскольку такой вызов заменил бы текушее состояние объекта первоначальным. В то же время при Часть й. Язык Сй+ передаче объекта в функцию нам нужен полный лубликат объекта, отражающий его текущее, а не первоначальное состояние. По завершении работы функции копия объекта выходит из области видимости, поэтому вызыяаеглсл его леструктор.
Вот почему в предыдущей програл|ме деструктор вызывался дважды. Сначала он вызывается, когда параметр функции ЛО выходит из своей области видимости, а затем — когда прн завершении работы программы уничтожается объект о. Подведем итоги. Если возникает необходилгость создать копию объекта и передать ее функции, обычный конструктор не вызывается.
Вместо него вызывается автоматический конструктор копирования, создающий побитовый дубликат объекта. Однако при уничтожении копии объекта (хак правило, в момент возвращения из функции) вызывается его деструктор. Поскольку автоматический конструктор копирования созлает точную копию оригинала, он может стать источником проблем. Лаже если объект передается функции по значению, что теоретически позволяет защитить аргумент от изменения, возможны побочные эффекты.
Например, если объект, передаваемый как параметр, выделяет динамическую память в момент своего создания и освобожласт ее при своем уничтожении, его локальная копия внутри функции будет освобожлать ту же самую область памяти при вызове деструктора. Это приведет к повреждению оригинала. Для того чтобы прелотвратить появление таких проблем, в классе необходимо явно определить конструктор копирования (см. главу 14).
( 3 Возвращение объектов Функция может возвращать объект вызываю)цему модулю. В качестве примера проанализируем следующую программу. // Возвражеиие объектов из Функций. Въпс1Ые <ъояггеагл> ияъпд папеярасе ясбг с1аяя п1ус1аяя ъпс 1; ро)з1 ъс: тоЫ яег 1(бпг и) ( ъ=п; ) Впс дес 1() ( гесигп 1) ) )' мус1аяя г(); // Вояврашеиие объекта класса иус1аяя. ъпс иаъп() ( тус1аяя о; о= г(); соог сс о.пег 1() « "1п"; геспгп 0; иус1аяя г() ( мус1аяя х; Глава 12.
Классы и объекты х.яес 1(1)) кесикп х; При возвращении из функпии автоматически создается временный объект, в котором хранится возвращаемое значение. Именно этот объект на самом деле возвращается в вызывающий людуль. После возвращения этот объект уничтожается. Это может привести к непредсказуемым послелствияль Например, если деструктор возвращаемого объекта должен освобождать динамическую память, она будет освобождаться, даже если объект, которому присваивается возвращаемое значение, по-прежнему ссылается на нее. Избежать этого можно с помощью перегрузки оператора присваивания (см.
главу 15) и определения конструктора копирования (см. главу 14). ~:~ Присваивание объектов Если объекты имеют одинаковый тип, их можно присваивать друг другу. При этом данные, содержащиеся в объекте, стоящем в правой части оператора присваивания, присваиваются переменным-членам объекта, стоящего в левой части. Проиллюстрируем этот механизм с помощью следующей программы, выводящей на экран число 99.
// Присваивание объектов. $(пс1ис)е <Ьояегеап> изъпд паиеярасе яс<); с1азя тус1аяз ( зпс рпЬ11с: чоЫ зес 1(ъпс и) ( 1=п; ъпе дее 1 О ( геессп (пс таъп() тус1аяя оЬ1, оЪ2) оЬ1.зес 1(99)г оЬ2 = оЬ1г // Присваивание данных объекта оЬ1 объекту оЬ2. соне « 'Это переменная 1 из объекта 1: " « оЬ2.оее 1(); геепгп Ог По умолчанию все данные из одного объекта присваиваются соответствующил~ переменным другого объекта с помощью побитового копирования.
Однако оператор присваивания люжно перегрузить и определить другую пропедуру (см. главу 15). лв Часть й. Язык С++ Полный спр 2ВОчник по В 6 $6 Массивы, указатели, ссылки и операторы динамического распределения пайяти Б первой части книги мы уже изучили указатели и массивы, а также их применение ко встроенным типам данных. Тспсрь рассмотрим их взаимолсйствие с объектами. Кремс того, обсудим новое понятие — ссылки, тесно связанныс с указателями. В завершсние ознакомимся с операторами динамического распределения памяти. Массивы объектов В языке С++ массивы могут состоять из объектов.
С синтаксической точки зрения объявление массива объектов ничем нс отличается от объявления массива встроенного типа. Вот как выглялит программа, в которой используется массив, состояший из трех элементов. Ф1пс1пйе <1ояттеатп> ояьпу пап1еярасе всдз с1аяв с1 ( ьпс зз рпЫ !.с: чои) вет з.
(з.пт 3) ( 1=3 з Зпс пес з. () ( тетитп з.з ) )з 1пс па1пО ( с1 оЬ[3] з ьпт ьз тот(з.=оз з.<зз з.++) оЬ(з.).вес з.(1+1) Гот(1=0; 1<3з 1++) соус « оЬ(1).пес 1() « " тп"з тетотп Оз ) Эта программа выводит на экран числа 1, 2 и 3.