Б. Страуструп - Дизайн и Эволюция C++. 2006 (1160775), страница 74
Текст из файла (страница 74)
главу 17), Статические функции-члены оказались среди тех свойств языка, которые были единодушно поддержаны во время дискуссий на семинаре разработчиков компиляторов в Эстес Парк (см. раздел 7.1.2). ИИВИИИИ Уточнения понятия класса с1авв Бсггпд ( с1авв Кер ( // ); кер* р; // Бггьпд - это описатель для кер всагьс 1пг соппг; // рпЫ1с; с)1ага орегагог[] (1пг 1); // ): // файл Бсг1пд.)1 (интерфейс): с1авв Бггфпд ( с1авв Кар~ Кер* р; // Бсгтпд — описатель для Кер всас1с 1пс соипс; // рпЫ1с: спать орегагог[](1пг 1); // )' // файл БГГьпд.с (реализация] с1авв Бсг1пд::Кер ( // ) всасьс 1пс Бсг1пд::соппг = 1; спать Бгггпд::орегагог[)(1пг 1) ( // ) Это расширение было принято просто как исправление недочета. Однако технику, которую оно поддерживает, не следует недооценивать.
Ведь многие продолжают чтобы оставить класс кер локальным. К сожалению, зто приводило к увеличению объема информации, необходимой для объявления классов, следовательно, к увеличению времени компиляции и частоты перекомпиляций. Слишком много интересной и изменяюшейся информации помешалось во вложенные классы.
Во многих случаях эта информация была неважна для пользователей такого класса, как Бгг1пд и ее следовало расположить в другом месте, наряду с прочими деталями реализации. Тони Хансен предложил разрешить опережающие объявления вложенных классов точно так же, как зто делалось лля функций-членов и статических членов: Ключевое слово!п)тег(тед ~чВИИИИИИИ перегружать заголовочные файлы всякой чепухой и страдают оттого, что переком- пиляция занимает много времени. Поэтому так важны любой прием, срелство, которые позволяют уменьшить зависимость пользователя от разработчика ком- пилятора. 13.б. Ключевое слово 1пЬегйед На одном из первых заседаний комитета по стандартизации Дзг Брюк предложил расширение, которым заинтересовались несколько человск 151гопзггц р, 1992Ы: «Многие иерархии классов строятся «инкрементно», путем расширения функциональности базового класса за счет производного.
Как правило, функция из производного класса вызывает функцию из базового, а затем выполняет некоторые дополнительные действия: вггцсг А ( чгггца1 чоЫ )таас)1е(1пг)г ); вггцсг О : А ( уо1й )ганг)1е(1аг)г чоЫ Огглапд1е(тпг. 1)г ( Аггпап61е(1); // прочие действия чоЫ Ог идапц1е(тпс 1); 1п)гег1сег)гг(ганг)1е(1)г // прочие действия ) Квалификацию с помощью ключевого слова 1п)теггсег) допустимо рассматривать как обобщение квалификации именем класса. Тогда решается целый ряд связанных с этим потенциальных проблем, что особенно важно лри сопровождении библиотек классов». Я обдумывал зто предложенис на ранних стадиях проектирования С++, но предпочел квалификацию именем базового класса, поскольку такой способ мог работать множественным наследованием, а 1п)тег1сес) г г — нет.
Однако Дзг отметил, что описанные проблемы можно решить, сочетая обе схемы: «По большей части лри разработке иерархий имеется в виду одиночное наследование. Если изменить дерево наследования так, что класс О наследует одновременно А и В, то получим: вггцсг А ( чтгвца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пс()г // ) Дальнейшее обсуждение данного примера можно найти в 12лг), стр.
2051. Мы не заметили, что восстановление в С++ вложенных классов открыло возможность управления областью действия и разрешением имен типов точно так же, как и любых других имен. В таком случае Лг г Ьепг)1е () — корректная конструкция С++, хотя, возможно, и неправильная. Использование ьппегьсеаг: Ьвпс)1е ( ) здесь неоднозначно и вызывает ошибку во время компиляции. Я думаю, что такое поведение желательно, ибо заставляет пользователя, объединяющего иерархии, явно разрешать неоднозначность. С другой стороны, донный пример показывает, что при использовании множественного наследования полезность ключевого слова 1ппег1сес) г г ограничена». Меня убедили эти доводы, к тому же сопроводительная документация была тшательно продумана.
Таким образом, предложение полезно, нетрудно для понимания и реализации. Имелся и опыт применения, поскольку некий подобный вариант был реализован компанией Арр!е на основе экспериментов с ОЪ1есс Разов!, а кроме того, это не что иное, как вариация на тему слова энарес в Бша!11аПс. Но вслед за последним обсуждением предложения в комитете Дэг сам посоветовал включить это в книгу как хрестоматийный пример хорошей, но непринятой идеи [91гопзггпр, 1992Ь1: «Предложение хорошо аргументировано... В данном случае его реализовывал представитель от компании Арр)е. При обсуждении мы быстро пришли к выводу, что серьезных изъянов нет.
В отличие от более ранних предложений на ту же тему !некоторые из которых восходили еще к временам споров по поводу множественного наследования в ) 986 г.) оио корректно разрешало неоднозначности при использовании множественного наследования. Мы согласились, что реализовать расширение будет просто, а программистам оно будет весьма полезно. Заметим, что всего этого еще недостаточно, чтобы одобрит~ предложение.
Нам известно несколько десятков мелких усовершенствований вроде этого и, по крайней мере, дюжина крупных. Если бы мы приняли их все, то язык утонул бы из-эа собственной тяжести (вспомним корабль куаза»!1. Однако в какой-то момент обсуждения вошел Майкл Тиман, бормоча себе под нос что-то вроде «но нам такое расширение ни к чему; мы и беэ него умеем это делать».
Когда утихли возгласы «да нет же)», Майкл продемонстрировал нам следующее: Ослабление правил замещения ПИИИИИИБ 13.7. Ослабление правил замещения Рассмотрим, как можио иаписать функцию, возврашаюшую копию объекта. Если имеется копирующий конструктор, то это тривиально: с1авв В ( риЫго: чугспа1 В* с1опе() ( геспгп лен В(*сЫв); ) // ); Теперь можно клоиировать объект любого класса, производпога от В, если в ием замешена функция В: г с1опе. Например: с1азв Р : рпЫ1с В ( риЫтс: // старое правило: // с1опе() должна возвращать В*, если хачет заместить Вг:а1опе() В* с1опе() ( гегпгп пен Р(*СЫв)т ) чо1б Ь(); // чоэа б(В* рЬ, Р* рс)) ( в* рЬ1 = рЬ->с1опе(); В* рЬ2 = рб->с1опеи; // ) // РЬ2 указывает на Р Увьд знание о том, что рс) указывает иа Р (или иа объект какого-то класса, производиого от Р), утрачено: чо1б а(Р* рс)) ( В* рЫ = рд->с1опе(); Р* рб1 = рд->с1опе()т рб->с1опе()->Ь()г // правильно // ошибка: с1опе() возвращает В* // ошибка: с1опе() возвращает В* // неудачный обходной путь Р* рд2 = (Р*)рб->с1опе()г ((Р*)рб->с1опе())->Ь(); Увидев, что такой прием есть, мы решили направит~ усилия на роботу над каким.
нибудь другим аспектом стандарта. Преимущества ьппегакебг: как встроенного средство не перевешивали того факто, что программист и ток мог использовать определенное ключевое слово для обозначения базового класса, применяя уже имеющиеся возможности. Поэтому было решено не принимать расширение эппег1сес):: я.
ПШЗИИИИИ6~( Уточнения понятия класса с1авв о: рпЫ(с в ( рпЫьс: // заметим, с1опе() возвращает О* О* с1опе() ( геевтп пеи П(*СЫв); ) ло(б Ь(); // ); ло(б дд(3* рЬ, О* рб) ( в* рЫ = рб->с1опе(); о* рб1 = ра->с1оле О; рб->с1оге()->Ь(); // правильно // правильно // правильно о* рцз = рЬ->с1опе(); рЬ->с1опе()->Ь(); // ощибка (как л раиьще) // осибка (как л раиьще) Это расширение — предложил Алан Снайдер (А!ап 5пу((ег) — стало первым официально представленным на рассмотрение комитета и было принято в 1992 г.
Но прежде чем одобрить его, следовало ответить на два вопроса: ы пс вызовет лн расширение каких-либо серьезных проблем в реализации (скажем, связанных с множественным наследованием )щи указателями на члены)? ;з какис преобразования, которые можно было бы применить к типу значения, возвращаемого замсшающей функцисй, стоит рассматривать? Первый вопрос меня не очень волновал: я думал, что знаю, как реализовать ослабление правил в стандартном случас. Однако Мартина О'Риордана это беспокоило, поэтому он представил комитету детальное письменное обоснование возможности реализации. Трудней всего было решить, стоит ли вообп(е заниматься ослаблением и лля каких именно преобразований.
Часто ли возникает необходимость вызывать вцртуальныс функции для обьекта производного тина и выполнять операции над возвращаемым значснисм, имеющим этот тип? Несколько человек, в особенности Джон Бранс (')опп Вгцпз) и Билл Гиббонс, уверенно доказывали, что такая необходим~)сть возникает постоянно, а не только в примерах из теории компиляторов, вроде с1опе. В конце концов мспя убедило сделаннос Тедом Голдстсйном (Те(( Со!баге(п) наблюдение, что почти две трети всех приведений типов в программе из 100 тыс. строк, пад которой он работал в Япп, были нужны только для того, чтобы обойти трудности, не возник)пис бы при ослаблении правила замещения.