|
||||
|
R.18 Приложение B: СовместимостьЭто приложение не относится к справочному руководству C++ и не является определением конструкций языка. Язык C++ основывается на С (описание в книге Кернигана и Ритчи, 78 г., дальше K&R) и включает большинство изменений, предложенных в ANSI стандарте для С. При конвертировании программ на языках С++, K&R C и ANSI C могут возникнуть трудности в связи с различным вычислением в них выражений. Транслятор должен распознавать все различия между C++ и ANSI C. Программы на C++ и ANSI C должны иметь одинаковый смысл за исключением трех следующих случаев: • В языке С выражение sizeof('a') равно sizeof(int), а в C++ оно равно sizeof(char). • Если есть описание enum e { A }; то sizeof(A) равно в С sizeof(int), тогда как в C++ оно равно sizeof(e) и не обязано быть равно sizeof(int). • Имя структуры, описанной во внутреннем блоке, может скрывать имя объекта, функции, элемента перечисления или типа из внешнего блока. Приведем пример: int x[99]; void f() { struct x { int a; }; sizeof(x); /* для C это размер массива */ /* а для C++ размер структуры */ } R.18.1 РасширенияВ этом разделе перечисляются основные расширения языка С, введенные в С++. R.18.1.1 Возможности С++, введенные в 1985 г.Здесь перечисляются возможности, добавленные к С, версией языка C++ 1985 г. • Можно указывать типы формальных параметров функции (§R.8.2.5), и они будут проверяться (§R.5.2.2). Будет происходить преобразование типа (§R.5.2.2). Это есть и в ANSI C. • В выражениях со значениями типа float вычисления могут проходить с обычной точностью (§R.3.6.1 и §R.4.3). Это есть и в ANSI C. • Можно перегружать имена функций; §R.13. • Можно перегружать операции; §R.13.4 • Возможна реализация вызова функций подстановкой; §R.7.1.2. • Можно описывать объекты, представляющие данные, со спецификацией const; §R.7.1.6. Это есть и в ANSI C. • Можно описывать типы ссылки; §R.8.2.2 и §R.8.4.3. • Возможно управление свободной памятью с помощью операций new и delete; §R.5.3.3 и §R.5.3.4. • Введены классы, которые позволяют: скрывать информацию (§R.11), проводить инициализацию (§R.12.1), осуществлять пользовательские преобразования типа (§R.12.3) и работать с динамическими типами с помощью виртуальных функций (§R.10.2). • Имя класса или перечисления считается именем типа; §R.9. • Указатель на любой объект c типом, не являющимся const или volatile, можно присвоить указателю типа void*. Это есть и в ANSI C. • Указатель на функцию можно присваивать указателю типа void*; §R.4.6. • Описание внутри блока считается оператором; §R.6.7. • Можно описывать безымянные объединения; §R.9.5. R.18.1.2 Возможности, добавленные в C++ после 1985 г.Здесь перечисляются основные расширения C++ после 1985 г.: • Класс может иметь более одного прямого базового класса (множественное наследование); §R.10.1. • Члены класса могут быть защищенными; §R.11. • Операции new и delete можно описывать в классе и перегружать; §R.5.3.3, §R.5.3.4, §R.12.5. Это позволило определенный способ управления памятью для класса с помощью "присваивания указателю this" отнести в раздел анахронизмов; §R.18.3.3. • Можно явно уничтожать объекты; §R.12.4. • Присваивания и инициализация для класса определены как присваивание и инициализация по членам; §R.12.8. • Служебное слово overload стало излишним и отнесено к разделу анахронизмов; §R.18.3. • Произвольные выражения разрешены в качестве инициализаторов статических объектов; §R.8.4. • Объекты, представляющие данные, могут быть volatile; §R.7.1.6. Также и в ANSI C. • Допустимы инициализаторы для статических членов класса; §R.9.4. • Функции-члены могут быть статическими; §R.9.4. • Функции-члены могут быть const или volatile; §R.9.3.1. • Можно явно указать связывание с подпрограммами на других языках; §R.7.4. • Можно перегружать операции -›, -›* и `; §R.13.4. • Классы могут быть абстрактными; §R.10.3. • Для пользовательских типов префиксные и постфиксные операции различаются. • Шаблоны типов; §R.14. • Управление особыми ситуациями; §R.15. R.18.2 C++ и ANSI CВообще язык C++ обладает большими возможностями и налагает меньше ограничений, чем ANSI C, поэтому большинство конструкций ANSI C являются законными для С++, причем смысл их не меняется. Исключения сводится к следующему: • Любая программа на ANSI C, использующая в качестве идентификаторов следующие служебные слова С++, не является программой на С++; §R.2.4: asm catch class delete friend inline new operator private protected public template try this virtual throw • Хотя это считается устаревшем в ANSI C, реализация С может налагать драконовские ограничения на длину идентификаторов; в реализациях C++ это недопустимо; §R.2.3. • В C++ функция должна быть описана прежде, чем ее можно вызвать; §R.5.2.2. • Описание f(); в C++ означает, что функция f не имеет параметров (§R.8.2.5), а в С это означает, что f может иметь любое число параметров любого типа. Такое описание считается устаревшим в ANSI C. • В ANSI C можно несколько раз описать без спецификации extern глобальный объект данных, в C++ возможно только одно его определение; §R.3.3 • В C++ класс не может иметь тоже имя, что и имя typedef, относящееся в той же области видимости к другому типу; §R.9.1. • В ANSI C операнд типа void* можно использовать в правой части присваивания, а также при инициализации переменной типа указателя на произвольный тип; в C++ это невозможно §R.7.1.6. • В ANSI C возможны команды переходов, обходящие инициализацию; в C++ это невозможно. • В ANSI C по умолчанию глобальный объект типа const подлежит внешнему связыванию; для C++ это не так; §R.3.3. • Определения функций в "старом" стиле и вызовы неописанных функций считаются в C++ анахронизмами, которые не обязательно должны поддерживаться любой реализацией; §R.18.3.1. В ANSI C они просто считаются устаревшими. • В C++ структура (struct) образует область видимости (§R.3.2); В ANSI C структура, перечисление или элемент перечисления, описанные в структуре поднимаются в область видимости самой структуры. • Присваивание объекту типа перечисления значения, не принадлежащего перечислению, считается в C++ анахронизмом и не должно поддерживаться во всех реализациях; §R.7.2. В ANSI C рекомендуется для таких присваиваний выдавать предупреждение. • Строки, инициализирующие символьные массивы, не могут быть длиннее этих массивов; §R.8.4.2. • Тип символьной константы в C++ есть char (§R.2.5.2) и int в ANSI C. • Тип элемента перечисления есть тип этого перечисления в C++ (§R.7.2) и тип int в ANSI C. Кроме того, стандарт ANSI для С допускает значительные различия в допустимых реализациях языка, что может привести к еще большим расхождениям между реализациями C++ и С. В частности, в некоторых реализациях С могут быть допустимы некоторые несовместимые описания. В C++ требуется совместимость даже для разных единиц трансляции; §R.3.3. R.18.2.1 Как бороться с расхождениямиВ общем случае программа на C++ использует многие возможности, отсутствующие в ANSI C. Для такой программы незначительные расхождения, перечисленные в §R.18.2, явно перекрываются расширениями в С++. Когда C++ и ANSI C должны иметь общие заголовочные файлы, нужно позаботиться, чтобы эти файлы представляли текст на общем подмножестве этих языков. • Нельзя пользоваться специфическими возможностями C++ такими, как классы, перегрузка и т.п. • Нельзя использовать одно имя для обозначения типа структуры и другого типа. • Функцию без параметров следует описывать как f(void), а не просто f(). • Глобальные объекты типа const следует явно специфицировать как static или extern. • Для разделения частей программы на ANSI C и C++ можно использовать условную трансляцию с предописанным именем __cplusplus. • Функции, которые могут вызываться из программ на обеих языках, должны быть явно описаны, как функции, подлежащие связыванию с С. R.18.3 АнахронизмРеализация C++ может включать перечисленные здесь расширения, чтобы облегчить использование программы на С, или чтобы упростить переход с более ранних версий С++. Отметим, что с каждым расширением связаны нежелательные последствия. Если реализация предоставляет такое расширение, то она должно также предоставлять возможность убедиться в отсутствии этих последствий для исходной программы. Реализация C++ не обязана обеспечивать эти расширения. • При описании или определении функции можно использовать слово overload в конструкции спецификация-описания (§R.7). Если оно используется в спецификации-описания, то считается служебным словом и его нельзя использовать как идентификатор. • Определение статического члена класса, представляющего данные, для которого дана стандартная инициализация нулями (§R.8.4, §R.9.4), может быть опущено. • Можно использовать команды препроцессора старого стиля (до ANSI C). • Можно присваивать объекту типа перечисления значение типа int. • При удалении массива, тип которого не имеет деструктора, можно указывать число элементов; §R.5.3.4. • Одна функция operator++() может использоваться для перегрузки как префиксных, так и постфиксных операций ++; тоже верно для операции --; §R.13.4.6. R.18.3.1 Определения функций старого стиляМожно использовать синтаксис С для определений функций: старое-определение-функции: спецификации-описаний opt старый-описатель-функции список-описаний opt тело-функции старый-описатель-функции: описатель ( список-параметров opt ) список-параметров: идентификатор список-параметров , идентификатор Приведем пример: max(a,b) int b; { return (a‹b) ? b : a; } Если определенная таким образом функция не была описана ранее, то тип ее формальных параметров полагается (…), т.е. он не будет проверяться. Если она была описана, то тип должен согласовываться с типом, указанным в описании. Приведенный синтаксис нельзя использовать для определения функций-членов. R.18.3.2 Старый стиль задания инициализатора базового классаВ конструкции инициализатор-памяти (§R.12.6.2) можно не указывать имя-класса, обозначающее базовый класс при условии, что существует только один прямой (непосредственный) базовый класс. Поэтому в описании class B { //… public: B(int); }; class D: public B { //… D(int i): (i) {/*… */} }; будет вызываться конструктор B с параметром i. R.18.3.3 Присваивание указателю thisПрисваивая определенные значения указателю this, пользователь мог управлять выделением памяти для объекта некоторого класса. В конструкторе до использования членов класса можно было с помощью такого присваивания реализовать свой алгоритм выделения памяти. Присваивая в деструкторе указателю this нуль, можно было обойти стандартную операцию освобождения объектов класса. Кроме того, присваивание нуля в деструкторе отменяло неявные вызовы деструкторов для членов и базовых классов, например: class Z { int z[10]; Z() { this = my_allocator(sizeof(Z)); } ~Z() { my_deallocator(this); this = 0; } }; Если выделение памяти уже произошло (как бывает для членов и объектов auto или static), то при входе в конструктор this имеет ненулевое значение и значение нуль в противном случае. Вызовы конструкторов для членов и базовых классов произойдут только после того, как this получил значение. Если в конструкторе базового класса есть присваивание this, то новое значение this будет использоваться и в конструкторах производных классов, если они есть. Отметим, что при наличии указанного анахронизма или тип указателя this не может быть *const, или нужно делать исключение для this из правила о присваивании указателям со спецификацией const. R.18.3.4 Приведение указателей на функцию-членУказатель на функцию-член некоторого объекта можно привести к указателю на какую-то другую функцию, например (int (*) ())p-›f. Результирующий указатель будет настроен на функцию, вызов которой будет происходить с помощью обращения к этой функции-члену для того же объекта. Как обычно результат такого вызова считается неопределенным. R.18.3.5 Невложенность классовЕсли класс описан внутри другого класса и в программе больше не описано классов с этим именем, то его можно использовать, как если бы он был описан вне класса (так обстоит дело с описанием struct в С), например: struct S { struct T { int a; }; int b; }; struct T x; // означает `S::T x;' |
|
||
Главная | В избранное | Наш E-MAIL | Прислать материал | Нашёл ошибку | Наверх |
||||
|