Г. Шилдт - Полный справочник по C++ (1109478), страница 55
Текст из файла (страница 55)
Если в классе определен конструктор с параметрами, каждый объект в массиве инициализируется с помощью списка инициализации, как это принято для массивов любого типа. Однако точный вид списка инициализации зависит от количества параметров конструктора. Если конструктор имеет лишь один параметр, можно просто задать список начальных значений, используя обычные синтаксические конструкции, предназначенные для инициализации гаассивов. По мере создания элементов массива эти значения поочередно присваиваются параметру конструктора. Вот как выглядит слегка измененная версия предылушей программы.
Ипс1пс)е <1овттеааз> ояьпй пазаеврасе всс(з с1аяя с1 ьпс з.з рцЫ з.с з с1(ьпт 3) ( 1=3; ) // Конструктор 1пт дет 1 О ( тетстп )з ) )з Часть )!. Язых С++ ).пс та).п() с1 аЬ(3) = (1, 2, 3); // Список инициализации зп тот(1=0/ 1<Зз 1++) соцт « оЬ(1),дет 1() « "1п"з теготп О: ) Как и прежде, зта программа выводит на зкран числа 1„2 и 3. Фактически список инициализации, показанный в втой программе, является сокрашенным вариантом более сложной формы: )( с1 оЬ[З) = ( с1(1), с1(2), с1(З) ) з Здесь конструктор оз вызывается явно. Разумеется, короткая форма инициализации используется чаще. В основе этого способа лежит возможность автоматического преобразования типа, если конструктор имеет только один аргумент (см.
главу ) 2). Таким образом, сокрашенную форму инициализации лгожно использовать, только если массив состоит из объектов, конструкторы которых имеют лишь один аргумент. Если конструктор объекта имеет несколько аргументов, следует применять полную форму инициализации. Рассмотрил» пример. Ктпс1цз)е <1овттеалз> цвъпд пазкеврасе все)з с1авв с1 зпт Ьз з.пс рцЬ11сз с1(апс 3, зпс к) ( ь=зз 1=Из ) // конструктор с двумя параметрами апт дет т() (тетцтп тз) ьпт дег. Ь() (тесцтп Ьз) Ьпт лза 1п ( ) ( с1 ОЬ(3) ( с1(1, 2), // Инициализация с1(3, 4), с1(5, 6) )з Ьпс йот(1=0з з<Зз з++) соцт « оЬ(з).дес Ь()з соцт « соцт « оЬ(з).дет з.() « "'и"з тетотп 0; Здесь конструктор массива о1 имеет два аргумента. Следовательно, следует применять не сокрашенную, а полную форму инициализации, Глава зЗ.
Массивы, указатели, ссылки и операторы динамического распределения памяти 28г Инициализированные и неинициализированные массивы Особая ситуация возникает, когла необходимо аоздать как инициализированные, так н неинициализированные массивы объектов. Рассмотрим следуюшнй класс. с1азз с1 ъпс рцЬ1зсз а1(1па 3) [ з.=зз ) // Конструктор зпс оес 1() ( тесцхп зз ): Здесь в классе е1 определен конструктор с одним параметром. Это значит, что любой массив такого типа должен быть инициализирован.
Таким образом, массив нельзя объявить обычным образом. $ с1 а[9]з // Ошибка, конструктору необхопим список инициализации Этот оператор не работает, поскольку прелполагается, что класа а1 не имеет параметров, так как в обьявлении массива не указан аписок инициализации. Однако класс с1 не содержит конструкторов, не имеюших параметров. Итак, поскольку ланному объявлению не соответствует ни один конструктор, компилятор выдаст сообщение об ошибке. Чтобы решить эту проблему, необходимо перегрузить конструктор, добавив вариант, не имеющий параметров, как показано ниже. Теперь в программе можно объявить оба вила маасива.
с1авз с1 зпс дз рцЬ[зс: с1() ( з=оз ) // Вызывается лля неинициализированных массивов а1(зпс 3) ( 1=3з ) // Вызывается для инициализированных массивов ).пс пес 1() ( гесцтп з.з ) )з В таколз варианте программы допускаются следуюшие операторы. а1 а1[3] = [3, 5. 6)," // Инициализированный маасив с1 а2[34]; // Неинициализированный массив ~~О'-' Указатели на объекты Указатели могут ссылаться не только на переменные встроенных ~илов, но и на объекты. Лля лоатупа к членам класса через указатель на объект используется оператор "->'*, а не ".".
Проиллюстрируем это с помошью аледуюшей програмлзы. ()зпа1цс)е <зозсгеаш> цз1пд пашезраае асс[; с1ава с1 зпс 1; рцЬ1за: а1(зпе 3) ( з.=з ) зпс пес 1() ( гесцгп дз ) )з дпс ша1п() ( 1 оЬ(ВВ), *рЗ Часть (1. Язык С++ р = аоЬз // Получаем адрес объекта оЬ. соне « р->две 1()з // дяя вызова функции дее 1() // применяется оператор -> деспгп 0; Как известно, при увеличении указателя на единицу он перемешается на следуюший элемент того же типа. Например, целочисленный указатель будет ссылаться на следуюшее целое число.
Как правило, адресная арифметика зависит от типа указателя. (Иначе говоря. она зависит от типа данных, на который ссылается указатель.) Это правило касается и указателей на объекты. Например, слелуюшая программа использует указатель лля доступа ко всем трем элементам массива оЬ, Мьпс1пбе <ьоэсгеат> свапд пазаеврасе все)з с1авв с1 ( зззс ьз рсЬ11сз с1() ( 1=0з ) с1(ьпе 3) ( з.=зз ) 1пе дес Ь() ( тетьсп ьпс паап() ( с1 оЬ(3) = (1, 2, 3) з с1 рз зпс р = оЬз // Установить указатель на первый элемент массива. сот(1=От 1<зз 1++) ( сосо «р->дес ь () « "1п" з р++з // Указатель на сяедуюязий объект.
теестп Оз Указателю можно присвоить алрес открытого члена объекта, а затем ссылаться на этот член с его помошыо. Рассмотрим следуюшуто программу, которая выводит на экран число 1. Льпс1пбе <ьовгсеаэ> иэапд паязеврасе всат с1аэв с1 ( риЬ11сз зпс с1(зпе 3) ( 1=3з 1пг. пзаапИ ( с1 оЬ(1) з ьпс р; Глава!3. Мвссивы, указатели, ссылки и операторы динамическаго распределения памяти ИЗ р = аоЬ.1; // Получить адрес члена оЬ.1. сопс « р; // Обрашеиие к члену оЬ.1 через указатель р Геепгп О) Поскольку указатель р ссылается на целое число, он имеет тип 1пе.
В данном случае не имеет значения, что переменная 1 является членом обьекта оЬ. - ~ Проверка типа указателей Работая с указателями, следует иметь в виду: присваивать можно лишь указатели совместимых типов. *': 1пе *ра; 11оас *рт) Следующий оператор неверен. В рз =- рйг // Ошибка — несовместимость типов. Разумеется, лвзбую несовместимость можно преодолеть, используя приведение типов, однако зто нарушает принпипы строгой проверки типов.
~~~/ Указатель й1в При вызове функпии-члена ей неявно передается указатель на вызывающий объект. Этот указатель называется еьаа. Рассмотрим программу, в которой описан класс рит, предназначенный лля вычисления степени некоторого числа, $1пс1иг(е <1озсгеаш> иа1пд патеарасе зсй; с1ава риг ( г(опЬ1е Ь; е; боиЬ1е ча1; рпЬ11с: риг[бопЬ1е Ьаее, 1пе ехр); е(оиь1е дее рих'() ( гесьгп уа); ) риг::рит(г)оиЬ1е Ьаве, ъпе ехр) < Ь = Ьаае) е = ехр; ча1 = 1г 1ь(ехр==о) геспгп; год( ; ехр>О; ехр †) ча1 = ча1 * Ь; апс шазп() ( рьт [Е О, 2), У(2.5, 1), з(5.7, О) г соцс « х.дед рит[) « Часть й.
Язык С++ соус « у.дес рис О <с сосо « я.пес риг() « ")и"г сессгп Ог Внутри класса к функции-члену можно обращаться напрямую, не используя обьекты и название класса. Таким образом, внутри конструктора рис О оператор означает, что переменной Ь, принадлежащей вызываюшему объекту, присваивается значение переменной ьаяе. Однако тот же самый оператор можно переписать иначе. Указатель сИя ссылается на объект, вызываюший функцию рис(). Таким образом, выражение сИв->Ь ссылается на переменную Ь, принадлежащую текушему объекту. Например, если функция рис() вызвана объектом х (в объявлении х(л.о, а)), то указатель еИя в предьшушем операторе будет ссьшаться на объект х.
Впрочем, этот оператор можно записать в сокрашенном виде, не используя указатель «Ыя. Рассмотрим полное определение конструктора рис О, написанное с помошью указателя ЕЬ1я. рис::рис(с)оиЬ1е Ьаяе, 1пс ехр) ( гнз.я->Ь = Ьаяе; сИя->е = ехр; СЬ1я->ча1 = 1; Н (ехр==О) гесигп; (ог( ; ехр>О; ехр--) СИя->ча1 = СИя->ча1 " СИя->Ь; На самом леле ни один программист на языке С++ не станет писать конструктор таким образом, поскольку сокрашенная форма намного проше.
Однако указатель ЕЬ1я очень важен при перегрузке операторов, а также в ситуациях, когда функция- член должна испольювать указатель на вызываюший объект. Помните, что указатель сЫя автоматически передается всем функциям-членам. Следовательно, функцию пес рисО можно переписать иначе. Г( с)оиЬ1е пес рьт() ( гесисп сИя->ча1; В этом случае, если функция пес рис() вызывается с помошью оператора Л у.яее рис()г, указатель сЫя будет ссылаться на обьект у.
В заключение сделаем два важных замечания. Во-первых, дружественные функции не являются функциями-членами, и, слеловательно, им не передается указатель сЫя. Во-вторых, статические функции-члены также не получают указатель снзя. П Указатели на производные типы Как правило, указатель одного типа не может ссылаться на объект другого типа.
Однако у этого правила есть важное исключение, касающееся производных классов, Предполсским для начала, что в программе обьявлены два класса: в и р. Кроме того, Глава 13. Массивы, указатели, Осы)ии и Операторы динамичыжого распределения памяти ЛБ допустим, что класс и является производным от базового класса в. В этом случае указатель типа в* может ссылаться и на объекты типа и.