|
Программирование >> Инициализация объектов класса, структура
(a) Queue : List очередь : список (b) EncryptedString : String зашифрованная строка : строка (c) Gif : FileFormat (d) Circle : Point окружность : точка (e) Deue : Queue, List реализации: (f) DrawableGeom : Geom, Canvas рисуемая фигура : фигура, холст Упражнение 18.7 Замените член IntArray в реализации PeekbackStack (см. раздел 18.3.1) на класс deque из стандартной библиотеки. Напишите небольшую программу для тестирования. Упражнение 18.8 Сравните композицию по ссылке с композицией по значению, приведите примеры их использования. 18.4. Область видимости класса и наследование У каждого класса есть собственная область видимости, в которой определены имена членов и вложенные типы (см. разделы 13.9 и 13.10). При наследовании область видимости производного класса вкладывается в область видимости непосредственного базового. Если имя не удается разрешить в области видимости производного класса, то поиск определения продолжается в области видимости базового. Только ссылка или указатель на объект DisplayManager позволят нам полиморфно манипулировать его подтипами. Иначе говоря, объектно-ориентированное программирование поддерживается только композицией по ссылке (подробнее см. [LIPPMAN96a].) Теперь нужно решить, должен ли член класса ZooAnimal быть ссылкой или указателем на DisplayManager: член может быть объявлен ссылкой лишь в том случае, если при создании объекта ZooAnimal имеется реальный объект DisplayManager, который не будет изменяться по ходу выполнения программы; если применяется стратегия отложенного выделения памяти, когда память для объекта DisplayManager выделяется только при попытке вывести объект на дисплей, то объект следует представить указателем, инициализировав его значением 0; если мы хотим переключать режим вывода во время выполнения, то тоже должны представить объект указателем, который инициализирован нулем. Под переключением м1 понимаем предоставление пользователю возможности выбрать один из подтипов DisplayManager в начале или в середине работы программы. Конечно, маловероятно, что для каждого подобъекта ZooAnimal в нашем приложении будет нужен собственный подтип DisplayManager для отображения. Скорее всего мы ограничимся статическим членом в классе ZooAnimal, указывающим на объект DisplayManager. Упражнение 18.6 Объясните, в каких случаях имеет место наследование типа, а в каких - наследование class ZooAAnimal { public: ostream Sprint( ostreamS ) const; сделан! открыт только ради демонстрации разн случаев string is a; int ival; private: double dval; определение класса ZooAnimal: class Bear : public ZooAnimal { public: ostream Sprint( ostreamS ) const; сделаны открытыми только ради демонстрации разных случаев string name; int ival; и упрощенное определение производного класса Bear: Bear bear; Когда мы пишем: bear.is a; то имя разрешается следующим образом: bear - это объект класса Bear. Сначала поиск имени is a ведется в области видимости Bear. Там его нет. Поскольку класс Bear производный от ZooAnimal, то далее поиск is a ведется в области видимости последнего. Обнаруживается, что имя принадлежит его члену. Разрешение закончилось успешно. Хотя к членам базового класса можно обращаться напрямую, как к членам производного, они сохраняют свою принадлежность к базовому классу. Как правило, не имеет значения, в каком именно классе определено имя. Но это становится важным, если в базовом и производном классах есть одноименные члены. Например, когда мы пишем: bear.ival; Именно эта иерархическая вложенность областей видимости классов при наследовании и делает возможным обращение к именам членов базового класса так, как если бы они были членами производного. Рассмотрим сначала несколько примеров одиночного наследования, а затем перейдем к множественному. Предположим, есть упрощенное int ival; int Bear::mumble( int ival ) return ival + обращение к параметру ::ival + обращение к глобальному объекту ZooAAnimal: : ival + Bear::ival; коде): Неквалифицированное обращение к ival разрешается в пользу формального параметра. (Если бы переменная ival не была определена внутри mumble() , то имел бы место доступ к члену класса Bear. Если бы ival не была определена и в Bear, то подразумевался бы член ZooAnimal. А если бы ival не было и там, то речь шла бы о глобальном объекте.) Разрешение имени члена класса всегда предшествует выяснению того, является ли обращение к нему корректным. На первый взгляд, это противоречит интуиции. int dval; int Bear::mumble( int ival ) { ошибка: разрешается в пользу закрытого члена ZooAnimal::dval return ival + dval; Например, изменим реализацию mumble() : Можно возразить, что алгоритм разрешения должен остановиться на первом допустимом в данном контексте имени, а не на первом найденном. Однако в приведенном примере алгоритм разрешения выполняется следующим образом: (a) Определено ли dval в локальной области видимости функции-члена класса Bear? Нет. (b) Определено ли dval в области видимости Bear? Нет. ival - это член класса Bear, найденный на первом шаге описанного выше процесса разрешения имени. Иными словами, член производного класса, имеющий то же имя, что и член базового, маскирует последний. Чтобы обратиться к члену базового класса, необходимо квалифицировать его имя с помощью оператора разрешения области видимости: bear.ZooAnimal::ival; Тем самым мы говорим компилятору, что объявление ival следует искать в области видимости класса ZooAnimal. Проиллюстрируем использование оператора разрешения области видимости на несколько абсурдном примере (надеемся, вы никогда не напишете чего-либо подобного в реальном
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |