Б. Страуструп - Язык программирования С++. Специальное издание, 3-изд. Бином. 2004 (1160791), страница 100
Текст из файла (страница 100)
При учете этих затрат для анализа во всем остальном приемлемого подхода, очерченного выше, обратите внимание, что объект классариа1 я1Ыегобычно хранит только указатель на таблицу виртуальных функций. Как отмечено в ч' (5.2А, такой абстрактный класс, не хранящий переменных данных, можно делать повторяю- 455 15.2. Множественное наследование спимся, не опасаясь неприятных побочных эффектов.
Таким образом, мы можем заме- нить виртуальный базовый класс обычным; с!азз ВВ (иа! л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!с(ег. с1азз (г'спс/от ( 0-. исггиа! зе1 со!ог (Со!ос) = О; Ыстиа! иоЫ ргоспр1 () = О; ); //Остановки цвета фана с!азл Кспс!от тосЯ Ьогс!вг; риЫ!с Ыг(иа! Сисис!ою ( 0- ле1 со!ог(Со1ос(; // управление цветом фона, с!азз (ссспс!от ти(1Ь тели , риЫсс и!гсиа1 (Зспс(от ( О. иоп1 рготр1 (); /с' управление вэаииодейтпвием с пользователе и с!азз МО т!пс(от: риЬВс ((г!патою и!1Ь тени, риЫсс (с'спасите тЫЯ Ьогс!ег ( 0 ... 15.2.5.1. Замещение функций виртуальных базовых классов Производный класс может заместить виртуальную функцию своего непосредственного или косвенного виртуального базового класса. В частности, два различных класса могут заместить различные виртуальные функции виртуального базового класса.
Таким способом несколько производных классов могут внести свой вклад в реализацию интерфейса, представленного в виртуальном базовом классе, Например, класс (гспс(ою мог бы иметь функции зе1 со!ог() (установить цвет) и рготр1() (выдать приглапсение на ввод). В этом случае (Гсссс(ов юссй Ьогс(егмогбы заместить функцию зе! со1ог () для управлешия цветовой схемой, а И (пс1ою цсс1Ь тели мог бы заместить функцию ргоспр1 () для управления интерактивным взаимодействием с пользователем: 455 Глава 15.
Иерархии классов Что если различные производные классы заместят одну и ту же функцию? Тогда если мы порождаем от этих классов новый производный класс, мы должны в нем заместить эту функцию. То есть, одна функция должна замещать все остальные. Например, Мд Мпс(ов мог бы заместить рготр( () для улучшения интерфейса Вс(пс(ош ш!1п тели: с(авзМу т1ас(от ривйс 1Гс1ис1от ивгй тени, риЬ1(с 1»1ас)от тйв Ьосс1ес( О. ооЫрготрг(); О не оста вплел~ взаимодеа1ствие с пользовател ел 0 базовому классу или в графическом виде: 1Пийот тйЬ тели(рготр1(() 'йссийот тйд Ьогйес(ве! со!от() ) Если два класса замещают функцию базового класса, то порождение от них производного класса, не заметающего эту функцию, недопустимо.
В этом случае не может быть создана таблица виртуальных функций, потому что вызов этой фуцкпии с завершенным объектом будет неоднозначен. Например, если бы класс лкаЖо из з 15.2.4 не объявил шп1е (), объявления вг((е () в 1сесеп ее п 2еапвт(1(ег вызвали бы ошибку при определении Рис((о. Как и в случае с )гас((о, такой конфликт разрешается путем добавления замсщающей функции в самый <нижний» производный класс. Класс, который обеспечивает некоторую часть — но не всю — реализации виртуального базового класса, часто называют «примесью». 15.3. Управление доступом Член класса может быть закрытым (рс лзи1е), защищенным (рго1ес1ес1) илп открытым (риЫсс): Если он закрыт, его имя может использоваться только в функциях-членах и друзьях класса, в котором он объявлен.
Если он защищен, его имя может использоваться только в функциях-членах и друзьях класса, в котором он объявлен, и классов, производных от него (см, ~ 11.5). Если он открыт, его именем может пользоваться любая функпия. Это отражает ту точку зрения, что существует три вида функций, имеющих доступ к классу: функции, реализующие класс (члецы и друзья), функции, реализуюп1ие производные классы (друзья и члены производных классов) и все остальные функции. Это можно изобразить графически: 457 15,3.
Управление доступом пользователи функции-члены и друзья производных классов рузья самого класса Управленце доступом применяется одинаковым образом ко всем именам. То, к чему относится нмя, не оказывает влияние на управление его использованием. Это означает, что мы можем иметь закрытые функции-члены, типы, константы и т. д., так же как закрытые члены данных. Например, аффективному неинтрузивному Я 15.2.1) классу списков часто требуются структуры данных для учета элементов. Такую информацию лучше сделать закрытой; 1етр1а1ечс!окк Т> с!аяк Е!к! ( рпоаге. к1гис1Е!пй ( Тоа1;Етй" пех1; 1; к!гас! Сйипй( епит(сйипй к!яе= !51; Етй о(сйипй каке], Сйипй" пех1; Сйипй' а!!оса!ее(, Е!пй* 1гее; Е!пй" де! !гее (); Еичй' Аеас, риб!!с: с!аяк Е'обегает ( 1; 1/ класс исключения оо!б !пкег! (11; Тае! ((, 0 ' 1етр!а1е<с!акк Т оопй Егк1<Т>:йпкегг (Т оай ( 1.тй*1пй = ае! гее ((, !пй- са! = оа1, 1пй — >пех1 = Ьеад; йеад = !пй; 1етр!а1е<с!акк Т Е!ктсТ>сЕ!пй*Е!к1<Т>:.де1 аггее (( ( й' ((гее == О) ( О есло свободной памяти болыие нет /чч выделить новый касок бсйипй1 памяти // и поместоть его свяэн ! тй в список свободной памяти 1 Глава 15.
Иерархии классов 458 Пл!г' р =/гее, /гее =/гее->пех1; ге1игл р; 1етр!а1е<с!аяв Т> ТС!з1<Т>:яе1 (( ( !/ (Ьеад = 0) 1Ьгого 1!пдег/!от (); С!и!г* р = Ьеад, Ьеад = р — >лех1; р->лех1 =/гее; /гее = р; ге1игп р — >па!; Область видимости Е!з1<Т начинается с А!з1<Т>з в определении функции-члена. Так как возвращаемый тип функции йе1 аггее() упоминался до имени А!з1<Т>цйге! !гее !), должно использоваться полное имя ЕЫ<Т>зТ!л!г, а пе сокращенная форма/!пй«Т>.
Функпии-не-члены (за исключением друзей) не имеют подобного достугга: ио!с1тои!д Ье тедд!ег(С!зг«Т>'р) ( Егвг<Т .Е!пй" г(=д; // ошиокаг Сгзг<Т к! гп!г — закрытое гигя //огиабка: !!зг<Т>нйггй — закрыагоеиия гг = р — >!гее; //- Ц' (Е!зг<Т>зСЬиггй..сдипй гйее > 31) ( ,г/ отибка: Сгзг<Т>иСЬггпггисгггггг!г леев //закрьгтое иия //- ) В классе члены по умолчанию закрытые, а в структуре — открытые (в' 10.2.8). 15.3.1. Защищенные члены Рассмотрим пример с ((г!пг(ощ из д 15.2А.1.
Функции отоп д(ганг() спроектированы в качестве строительных блоков для использования в производных классах, и их небезопасно использовать в остальных случаях. Эту разницу можно выразить разбиением интерфейса классов (Гйи/ото ца две части — защищенную и открытую: с!азя (Тг!лдот иг!1Ь Ьогдег( риЫ!а игг1иа! по!6 дгаго (); //- ргогесгед оо!догоп дгат(); // ...какой-то вспомогательный код рыиаге 0 ар едсгпавл ение и и. д.
459 ! 5.3. Управление доступом Производный класс может осуществлять доступ к защищенным членагя базового класса только для объектов его собственного типа: с!авв ВиЯег( ргогесгеЫ: сЬаг а [! 28[; уу" В с!аве йшуес! ЬиОег риЬ!!с Визг( г'" ... '!); с!аее Сус!!с ЬиДег риЬВсВиЦег( !! ио(г(г у гпЬес! ЬсЯе~ р[( а[0! =О, О правильно: доступ к собственному заи!пшенному О члену к«асса Сусдс Ьифег О оигибка: доступ к засйищенному члену другого типа р->а[0[ = 0 1 ); Такой подход предохраняет от довольно тонких ошибок, которые мотли бы привести к тому, что один производный класс разрушат данные, принадлежащие другому производному классу. 15.3.1.1.
Использование защищенных членов Простая модель сокрытия данных «открытый/закрытый> хорошо работает для конкретных типов (5 10.3). Однако прп использовании производных классов существуют два вида пользователей класса: производные классы и «простая публика >. Члены и друзья, реализующце операции класса, работают с объектами классов от имени этих пользователей. Модель «открытый/закрытый» позволяет программисту различать между разработчиками н всеми остальными, но она не предусматривает особого обслуживания для производных классов.
Члены, объявленные рго!ес!ес(, более подвержены злоупотреблениям, чем тс, которые объявлены рг!оа!е. В частности, объявление данных защищенными, обычно свидетельствует об ошибке на этапе проектирования. Поме|цение значительной части данных в общий класс, доступный для всех производных классов, приводит к риску разрушения этих данных. Более того, так же как и открытые, защищенные данные не просто реструктурировать ввиду сложности нахождения всех случаев пх использования. Таким образом, защищенные данные приводят к проблемам сопровождения. К счастью, вы не обязаны использовать защищенные данные; члены классов по умолчанию закрыты и, как правило, это является наилучшим вариантом, В моей практике всегда находнлигь лучшие альтернативы, чем помещение значитвльного количества информации в общий класс для непосредственного доступа к пей из производных классов.