• R.18.1 Расширения
  • R.18.1.1 Возможности С++, введенные в 1985 г.
  • R.18.1.2 Возможности, добавленные в C++ после 1985 г.
  • R.18.2 C++ и ANSI C
  • R.18.2.1 Как бороться с расхождениями
  • R.18.3 Анахронизм
  • R.18.3.1 Определения функций старого стиля
  • R.18.3.2 Старый стиль задания инициализатора базового класса
  • R.18.3.3 Присваивание указателю this
  • R.18.3.4 Приведение указателей на функцию-член
  • R.18.3.5 Невложенность классов
  • 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 | Прислать материал | Нашёл ошибку | Наверх