Б. Страуструп - Дизайн и Эволюция C++. 2006 (1160775), страница 66
Текст из файла (страница 66)
Меня такая неопределенность не устраивала из-за перегрузки функций. Так, я должен знать, что объявлено в следующем примере: чогб 1(со1от*) / чо1б Е(уеньс1е*)/ Одна и та же функция, объявленная дважды, или две перегруженные функции? Уверен, что формулировки всегда должны быть четкими, а оставлять решение на усмотрение разработчика компилятора не стоит.
Аналогичный пример чопб 1(со1ог)/ чозб 1(иенрс1е)/ также должен интерпретироваться однозначно. В С и С+а в том виде, в каком последний существовал до выхода АКМ, зто считалось одной функцией, объяв- ленной дважды. Однако правильнее было бы рассматривать каждое перечисле- ние как отдельный тип. Например: чо1б й() // Саж ( Со1ог с = 2; // ошибка с = Со1ог(2); // правильно: 2 явно преобразуется в Со1ог 1пс 1 = с; // правильно: с неявно преобразуется в 1пс ) При обсуждении перечислений с программистами на С++ кто-нибудь всегда громко требовал именно такого решения проблемы.
Быть может, я и действовал чересчур стремительно, но принятый вариант является лучшим. Для тех стилей программирования, которые должен был поддерживать С++, нс было нужды в перечислениях, поэтому правила С перешли в С++ без изменения. Правда, в комитете А)к)Б1 С изменили или, если хотите, уточнили определение перечислений таким образом, что указатели на разные перечисления оказались разными типами: .6ИИИИИИМ Перечисления 11.7.1 Перегрузка на базе перечислений Я упустил из виду очевидный факт: раз кажпое перечисление — зто отдельный тип, определенный пользователем, следовательно, он имеет все те жс права, что и класс.
В таком случае, на базе перечислений можно перегружать операторы. Указал на это Мартин О'Риордан (Маг()п О'К)огс(ап) на заселании комитета А)ч51/1БО. Вместе с Дэгом Брюком они пролумалн все детю)н, н перегрузка на базе перечислений была включена в С++. Например: впала Яеавоп ( и!пгег, врг!пд, впапег, Га11 ); Яеавоп орегагог+е(эеавоп в) ( ви(гоп (в) ( саве и!псег: гессгп врг!пр; саве врг!пр: гегвгп вивпег; саве вппвпег: гегпгп га11; саяе га11: гегигп и!псег; ) 11.7.2.
Тип Воо1еап Одно из самых распространенных перечислений — это еппа Ьоо1 ( га1ве, ггпе ); В любой сколько-нибудь сложной программе можно встретить нечто похожее, например: ()йег!пе Ьоо1 спас ))йег!пе Воо1 !пг Гурейе! ипвгрпей гпс ВООЬ; Сурейе! епип ( Э, т ) Воо1еап; сопев ггие = 1; вйе2!пе тацд 1 ()йе!!пе га1ве (!тгпе) Примеры можно множить ло бесконечности. Однако семантика большинства вариантов слегка различается и при совместном использовании в программе нескольких вариантов возникает конфликт.
Конечно, данная проблема была известна давно. Естественно, прежде всего на ум приходит мысль определить перечисление. Олнако Дэг Брюк и Шон Корфилл изучили сотни тысяч строк на С++, и выяснилось, что в болынинствс случаев при использовании типа поо1еап необхолимо свободно преобразовывать сго в !пс и обратно. Поэтому определение поо1еап в ниле перечисления испортило бы слишком много программ. Так стоит ли вообще о нем беспокоиться, если: л тип поо1еап существует вне зависимости от того, сеть он в станларте С++ или нет; Я применял такие переключатели, чтобы избежать привелений типов и операций над целыми числами. ПИИИИИИВ Перегрузка П наличие множества конфликтующих друг с другом определений делает неудобным и небезопасным любой тип Воо1еап; а многие пользователи хотят иметь возможность перегружать функции на базе типа Воо1еап.
К моему удивлению, комитет АХБ1/1эО принял эти аргументы, поэтому Ьоо1 теперь входит в С++ как отдельный интегральный тип и имеет литералы Сгие и йа1зе. Ненулевые значения можно неявно преобразовать в сгце, а стае неявно преобразуется в 1. Нуль можно неявно преобразовать в 1 а 1 ее, а 1 а 1 не неявно преобразуется в О. За счет этого обеспечивается высокая степень совместимости. Глава 12. Мнежественнее наследеванне Потому что у тебя есть отец и мать. сагяр lаад. с++ 12.1.
Введение Множественное наследование — возможность иметь более одного прямого базового класса — зачастую считалось самой важной особенностью версии 2.0. В то время я не соглашался с этим, так как полагал, что совокупность улучшений системы типов гораздо важнее. Кроме того, не стоило включать в версию 2.0 множественное наследование.
Оно действительно необходимо в С++, но гораздо менее важно, чем параметризованные типы, а для некоторых даже парал1етризованные типы — нестоящая мелочь по сравнению с обработкой исключений. Но получилось так, что параметризованные типы появились только в версии 3.0, а исключения — еще позже. Лично мне параметризованных типов не хватало гораздо больше, чем множественного наследования. Работа над множественным наследованием проводилась именно в то время по ряду причин: оно хорошо согласуется с системой типов С++ без крупных расширений, а реализацию можно было осуществить в рамках С1гопг.
Следует учесть еще один фактор, абсолютно иррациональный: никто, по-видимому, не сомневался, что я смогу эффективно реализовать шаблоны. А вот множественное наследование, по общему мнению, с трудом поддавалось эффективной реализации. Например, рассуждая о С++, Брэд Кокс (Вгаг) Сох) в своей книге, посвященной ОЪ/есвте С, заявил, что добавить множественное наследование в С++ невозможно [Сох, 1986). Таким образом, мне был брошен вызов. Я не смог его не принять, потому что думал о множественном наследовании еще в 1982 г.
(см. раздел 2.13), а в 1984 г. нашел для него простую и эффективную реализацию. Полагаю, что это единственный случай, когда на последовательность событий оказала влияние мода. В сентябре 1984 г. я представил механизм перегрузки операторов в С++ на конференции 1Р1Р %02 4 в Кентербери [Бггоцзггцр, 1984Ъ). Там я встретил Стейна Крогдала (Иге)п Кгойг[аЫ) из университета Осло; Стейи как раз заканчивал работу над предложением о включении множественного наследования в %нш1а [Кгойг)аЫ, 1984).
Его идеи легли в основу реализации обыкновенных множественных базовых классов в С++. Уже позже стало известно, что почти идентичное ИЕИИИИИИВ Множественное наследование предложение для 8(шп!а уже рассматривалось ранее. Оле-Йохан Дал (О!е (оЬап РаЫ) рассматривал множественное наследование еше в 1966 г. и отверг его из-за неизбежного усложнения сборщика мусора в 8(ши!а !ПаЫ, 19881. 12.2.
Базовые классы с1авя ыу Цзвр1ауеб Саек г рцЫ(с г)1ьр1ауеб, рцъ1(с сая)г ( // с1аяя ягу Сая)г: рцЫ1с Сая)г ( // не наследует оьяр1ауеб // ): с1аяя жу д(яр1ауеб : рцЫ(с бьяр1ауеа ( // не наследует сая)с О ): Если использовать только одиночное наследование, то программисту доступны лишь два последних вариантаж В то время меня беспокоило, что библиотечные классы становятся слишком громоздкими, поскольку должны удовлетворять как можно болыпему числу требований.
Множественное наследование я рассматривал как потенциально важное средство организации библиотек с меньшим числом классов и зависимостей между ними. Пример с классами Сая)с и с(1яр1ауес) показывает, как можно представить свойства параллельности и вывода на экран с помощью отдельных классов, нс перекладывая работу на программиста.
«Неоднозначности разрешаются во время компиляции; с1авя А ( рцЫ1с: ча(а г(); /* ... */ с1аяя В ( рцЫ1с: чо1б (()т /* ... */ с1аяв С г рцЫ1с А, рцЫ(с В (/* Г() нет ... */ ); чо(б о() ( С* р; р->г()г ) // ошибка; неоднозначность Первая и самая важная причина для рассмотрения множественного наследования заключалась в следуюгцем: необходимо было обеспечить такое объединение двух классов в один, при котором объекты результирующего класса ведут себя, как объекты любого из базовых классов (Ягоияггцр, 1986): «Довольно стандартный пример использования множественного наследования — создание двух библиотечных классов аьяр1ауеб и сая)к для представления обьектов, одновременно находящихся под управлением диспетчера вывода на экран и обладающих свойствами сопрограммы, работающей под управлением планировщика. Тогда программист мог бы создавать такие классы: 11ИИИИИИИ Базовые классы В этом смыспе С++ отличается ат объектно-ориентированных диалектов Взр, поддерживающих множественное наспедование» [В(гааз(гор, ) 987).
Разрешение неоднозначности с помощью зависимости от порядка, когда предпочтение отлается гт: г 1 просто потому, что А встречается раньше в, я отверг, так как имел негативный опыт использования подобных зависимостей в других местах (см. разделы 11.2.2. и 6.3,1). Я отказался также от всех форм лннамического разрешения, помимо виртуальных функций, поскольку они не годятся для статически типизированного языка с жесткими требованиями к эффективности. 12.3. Виртуальные базовые классы Вот цитата из работы [8(гопз(гпр, 1987): «Кпосс может встречаться в направленном ацикпическом графе наспедования более одного раза: с1аяя Саяк: рцЫгс 1)пх ( /* ... *7 ); с1аяя йгвр1ауей: рцЫйс 11п)г ( 7* ... *7 ); с1аяя йдяр1ауей Саек рцЫ1с йгяр1ауей, рцЫзс Саек ( /* ...