Б. Страуструп - Язык программирования С++. Специальное издание, 3-изд. Бином. 2004 (1160791), страница 54
Текст из файла (страница 54)
Механизм и1лс1иг1е является средством манипулирования текстом. позволяющим собрать фрагменты исходного кода программы в одну единицу (файл) компиляппп. Директива Глава 9.Исходные файлы и программы 246 Негласное практическое правило гласит, что заголовочный файл может содержать: Это практическое правило не является требованием языка, Оно просто отражает разумный способ использования механизма и!пс!ийе для выражения физической структуры программы.
С другой стороны, заголовочный файл никогда пе должен содержат>и с!> аг ие1 (с!лаг' р) ( ге1игп 'р+ ч; ) !и! а; яьог11ЫЦ = ( 1,2,3), патеярасе(/* ...'/) ехрог1 1етр!а1екс1аяя Т> ! (Т! (/* Определения обычных функций Определения данных Определения агрегатов Неименованные пространства имен Экспортируемые определения шаблонов Заголовочные файлы обычно имеют расширение .!л, а файлы, содержащие функции пли определения данных, имеют расширение .с. Поэтому на них часто ссылаются как на ..!л-файл>я» и «с-файлы» соответственно. Так>хе общеприняты расширения .С, схх, .срр и .сс. В руководстве по вашему компилятору об этом имеется конкретная ипформаппя, Причина рекомендации помешать определения простых констант, но не определения агрегатов в заголовочные файлы заключается в том, что компилятору трудно избежать репликации агрегатов пз нескольких единиц трансляпип.
Кроме того, более простые случаи чаще встречаются и поэтому важнее для генерашгн хорошего кода. Мудрым шагом будет це слишком усердствовать при использовании и!пс1ийе. Мон рекомендации состоят в том, чтобы включать только полные объявления и л >ределения и делать это только в глобальной области видимости. блоках спецификации компоновки и в определениях пространств имен при конвертировании старого кода (6 9.2.2). Как всегда, стоит избегать фокусов с макроподстановьой. Одним пз моих самых нелюбимых занятий является отслеживание ошибок, вызванных макроподстановкой в нечто совершенно отличное от ожидаемого из-за наличия косвенно включенного заголовочного файла, о котором я в жизни ничего не слышал.
Именованные пространства имен Определения типов Объявления шаблонов Определения шаблонов Об ьявления функции Определения встроенных функций Объявления данных Определения констант Перечисления Объявления имен Директивы включения Макроопределения !!прективы условной компиляции Комментарии патеярасе !лг(/* */) я1гисброт1( лп1х, д; ), 1етр!а1екс!аяя Т> с1аяя У; 1етр!а1е<с1аяя Т> с!аяя (г(/' ... */); ех1егп !п1 я1г!еп (сопя! сйаг*); т '!!пе с!лаг «е1 (с!лиг* р) ( ге1игп "р++) ех1егп !п1 а; я1/!оа1р ' = 2.!й! а 92; епит й!и!л! ( гей, уе!!ош, йгееп ); с1аяя Ма1г!х, и !пс1ийе 'а!иог! Я т> и йене УЕ!сЯЮХ !2 иЯе/ ср1ияр!ия /* проверка на конеч г)>айна */ 247 9,2.
Коглпоновка 9.2.2. Заголовочные файлы стандартной библиотеки Средства стандартной библиотеки представлены через набор стандартных заголовочных файлов (9 16.12), Для указания заголовочных файлов стандартной библиотеки не требуется расширение. Онн распознаются как заголовочные файлы, потому что вместо синтаксиса №гпс1ис)е "..." используется форма записи №1пс1цс)е<...>. Отсутствие распгирения .)г не подразумевает какого-либо конкретного способа хранения заголовочных файлов. Заголовочный файл <тар> может храниться в виде текстового файла тар.)г в стандартном каталоге. С другой стороны, не требуется.
чтобы стандартные заголовочные файлы хранились обычным способом. Конкретная реализация компилятора может воспользоваться знаниями о стандартной библиотеке для оптпмпзаппи реализации стандартной бполиотекп п обработки стандартных заголовочных файлов. Например, реализация может знать о стандартной математической библиотеке (5 22.3), сделать ее встроенной и интерпретировать №)пегас)е<спга1)г> в качестве переключателя, который делает доступныьо| стандартные математические функции, не читая при агом никакого файла.
Для каждого заголовочного файла стандартной библиотеки языка С <Х.й>, имеется соответствующий стандартный заголовочный файл С "е <сХ>. Например, №гпс1игле<св1слго> обеспечивает то же, что и №гпс!ис)е<в1111о.)г>. Типичный в1111о )г будет выглядеть следующим образом: пф1еу" ср)ивр1ив гггг люлько дгя конлилялгоров Сч ь Ц 933) патеврасевгд( 1)сгггггндаргпнаядггслггогггека определена гггг в пристли нет в е инеи втд Я 3.2. 9) гггг фуггкгггггг лгу)о гг леют колтоновку, нринягпую в С 19 9.2.лг) ех1егп гС 1 №епд)г" l* *! т1 ргггггУ~сапвгс)галл ..
); гл * №г)г)ег' ср)ивр(ив 0- ив1пувгд. регию гг) дел аегп рог г 1)' достугг и ылг гг/в елодальноя пргктранстве няен I!- пел дг) Такпьг образом, реальные объявления (наиболее вероятно) используются совместно, но для того чтобы обеспечить совместное использование заголовочного файла С и С - приходится задействовать средства компоновки и пространств имен, 9.2.3.
Правило одного определения Каждый конкретный класс, перечисление, шаблон и т. д. должны быть определены в программе ровно один раз. С практической точки зрения зто означает, что должно существовать ронно одно определение, скажем, класса, находящееся где-то в одном единственном файле. К сожале- Глава 9злоходные файлы и программы 248 ««««Ие2№ № (а с(и«1е "е.(Р ооЫГ(5*р)(!*.. "/) плп в графическом виде: Г № си с1ис(е «11'ислользо нпю, правило языка не может быть таким простым.
Например, определение класса мо- ж«т быть составлено при помощи макрорасш прения (ох уж! ), и в то же время определе- ние класса может быть включено в два походных файла при помощи директивы № тс1ис(е (9 9.2.1). Хуже того, концепция «файла» пе является частью определения языка С или С»; сугдествуют реаплзации, которые не хранят программы в исходных файлах, Следовательно, правило стандарта, говорящее о том, что должно существовать уни- кальное определение класса, гпаблона и т. д. должно быть изложено в более сложной манере.
Это правило называют «правилом одного определения» (Опе-Оейщыоп Кп)е, О Л К). Л именно, два определения класса, шаблона или встроенной функции приемле- мы в качестве определения одной и той же сущности тогда и только тогда, когда [11 они находятся в различных единицах трансляции; [2) онп идентичны лексема за лексемой; [3) значение лексем одинаково в обеих единипах трансляции. Например: ««««1йе1, с: е1гис1 5 ( т1а; сааг 6; ); ооЫ1'(5'), 0 Ф!е2.с: з1 гие15 ( 1№1 а, е)«аг Ь, ); оо(с(у" (5" р) ( ««* ... '««) Правило ООК говорит, что этот пример допустим, и что в обоих исходных файлах под 5 подразумевается одна и та же структура. Однако, неразумно записывать определение дважды подобным образом. Кто-нибудь из сопровождающих 111е2 с естественным обра- зом предположид что определение 5 в Яе2.с являешься единственным и будет считать себя вправе изменять его.
Это может привести к трудно обнаружнваемым ошибкам. Целью правила ООК является реализация возможности включения определения класса в различные единицы трансляции пз одного общего исходного файла. Например: «1 111е 5. Й ' е1гтт1 5 ( т1а; айаг(л; ); ооЫ1 (5*); ««г')) 1е1. с « №тс1№«1е ей' О агчользоел««ИРД) 9.2 Компоновка 249 Приведем примеры трех способов нарушения правила 01Ж; ///Ие1.с: вбгис1 51 ( т1 а; сааг Ь; ); 1 вггис151(1лга;слагЬ,); //ошибка:ловотрноеопредехение Это является ошибкой, потому что структуру нельзя дважды определить в одной едпни- цетрансляции. //111е1.с; в1ги с1 о2 [ 1л1 а; ел аг Ь, ); //.аегз: вггис1 52 ( тга, сйаг ЬЬ; ), //ошнбко Это является ошибкой, потому что 52 используется в качестве имени структуры, у которой отличаются имена членов.
// Яе!.св 1урес(е/1л1Х, вггис1 53 (Ха; сдагЬ; ), //111е2,с: 1урее(е/сЬаг Х; в1гис153(Ха;сЬагЬ;); //ошнбко Здесь имеется два определения ЗЗ, которые идентичны лексема за лексемон, но пример являешься ошибочным, потому что смысл Хразличается в двух файлах, Проверка согласованности определений классов в различных единицах трансляции.как правило, находится вне пределов возможностей большинства реализаций С++. Как следствие, нарушение правила ООК может являться источником очень тонких ошибок.
К сожалению, техника помещения совместно используемых определений в за1оловочные файлы с последующим нх включением в исходный файл не предохраняет от последней формы нарушения правила. Локальные 1уредеу н макросы могут изменить смысл включаемых объявлений: //.Ие х)и вггис1 $ ( Рот1 а; с наг Ь; ); //~я1е1, г: №с(есле Ротг (л1 №тс1ис(е "в Ь' //" ///1е гол с(авв Рот1 ( /' ... '/ ); №1лс1ш1е 'в Ь" //- Наилучшей зашитой от подобных проблем является создание как можно более самодостаточных заголовочных файлов, Например, если бы класс Ро(л1 был объявлен в заголовочном файле з. Ь, ошибка была бы обнаружена. Определение шаблона можно включать в несколько единиц трансляции, пока выполняется правило ООК. Кроме того, экспортируемый шаблон можно использовать прп наличии только объявления; Глава 9гнсходные файлы и программы 250 О/1!еl.с: ехрог(1етр1а1е е1аев Т> Т Ли/ге (Т !) ( ге!игл !»1() ,(,/ Гг!е2.с: 1етр/а1е<е/аее Т> Тпвгее (ТГ) !и! и (т! () ( ге/игп !га!ее (1); ) ,(( ооъявяение Ключевое слово ехрог( означает «доступно нз другой единицы трансляции > Я ) 3.7).
9.2.4. Компоновка кода, написанного не на С+- ех/егп "С' и/(аг* я!геру (с/гаг', сопя! с/(аг») Эффект от такого объявления отличается от «обычного» объявления ех/егп и/(аг" и!геру (и/гаг', еопе/ и/(пг»/ только соглашением о вызове з/геру (). Директива ех1егл 'С" особенно полезна ввиду тесной взаимосвязи С и С ". Обратите внимание, что "С' в ех/е/л "С" определяет соглашение о компоновке.