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