|
Программирование >> Полиморфизм без виртуальных функций в с++
После очередной перепалки в comp.lang.c++ и comp.lang.c один из моих друзей заметил: Если проблема нулевого указателя для них самая страшная, то им просто повезло . Опыт показывает, что использование О для обозначения нулевого указателя на практике не вызывает сложностей. Но меня по-прежнему удивляет правило, согласно которому результат вычисления любого константного выражения, равный О, прини.мается в качестве нулевого указателя. Согласно этому правилу, 2-2 и -1 - нулевые указатели. Но ведь ясно, что присвоение 2+2 или -1 указателю - ошибка типизации. Безусловно, разработчикам компиляторов такое правило тоже не нравится. 11.2.4. Ключевое слово overload в первоначальном варианте в С-и- допускалось использование одного имени для обозначения двух функций только после объявления overload. Например: overload max; overload - устарело в 2.0 int max(int,int); double max(double,double); Я счел слишком опасным такое использование без явного декларирования намерения. Например: int abs(int); overload abs отсутствует double abs(double); раньше считалось ошибкой Тому было две причины: □ опасение необнаруженных неоднозначностей; □ потенциальная возможность того, что программа может быть некорректно скомпонована, если программист явно не объявит, какие функции следует перегружать. Первое соображение оказалось беспочвенным. Те немногие проблемы, с которыми все же пришлось столкнуться, устранялись за счет применения независимых от порядка правил разрешения перегрузки. Второе опасение действительно связано с общей сложностью, возникающей из-за правил раздельной компиляции в С, но к перегрузке это отношения не имеет (см. раздел 11.3). С другой стороны, сами объявления overload стали источником серьезной проблемы: нельзя объединить два независимо разработанных фрагмента программы, в которых одно и то же имя используется для разных функций, если в обоих случаях оно не объявлено как перегруженное. Но так обычно не происходит; чаще всего нужно перегрузить имя библиотечной функции С, объявленное в заголовочном файле, например: /* Заголовочный файл стандартной математической библиотеки math.h */ double sqrt(double); /* ... */ Заголовочный файл библиотеки для работы с комплексными числами complex.h overload sqrt; complex sqrt(complex); ... Теперь можно написать #include <complex.h> #include <math.h> но не #include <math.h> #include <complex.h> поскольку использование слова overload только во втором объявлении - ошибка. Эту проблему удастся смягчить, изменив порядок объявлений, введя ограничения на использование заголовочных файлов или вставки overload повсюду. Но все-таки гораздо лучшим решением представлялся отказ от объявлений overload и самого ключевого слова. 11.3. Типобезопасная компоновка Компоновка в С проста и абсолютно небезопасна. Вы объявляете функцию extern void f(char); И компоновшик связывает эту f со всем, что найдет. А обнаружить он может функцию с совершенно другим числом аргументов или даже вовсе не функцию. Обычно это приводит к ошибке во время выполнения (нарушению зашиты и т.д.). Особенно ггеприятными проблемы компоновки становятся при увеличении программы и интенсивном использовании библиотек. Программисты на С научились с этим справляться. Однако с появлением механизма перегрузки задачу надо было срочно решать, но так, чтобы оставалась возможность вызывать написанные на С функции без дополнительных сложностей и лишних затрат. 11.3.1. Перегрузка и компоновка До версии 2.0 проблема компоновки программ, написанных на смеси С и С-и-, решалась так: имя, сгенерированное для С-и-функции, должно по возможности быть таким же, как было бы сгенерировано для одноименной С-функции. Поэтому функция open () получила бы и.мя open в системе, где С не модифицирует имена, и ореп - в системе, где в начало добавляется подчеркивание, и т.д. Для перегруженных функций такой простой схемы недостаточно. Частично ключевое слово overload и было введено для того, чтобы отличить трудный случай от легкого (см. также раздел 3.6). В основе первоначального решения, как и последующих, лежала идея о том, чтобы закодировать информацию о типе в имени, с которым работает компоновшик (см. раздел 3.3.3). Чтобы оставить возможность компоновки с С-функцией, кодировались имена только второго и последующих вариантов перегруженной функции. Стало быть, программисту приходилось писать: overload sqrt; double sqrt(double); компоновщику доступно: sqrt complex sqrt(complex); компоновщику доступно: sqrt FVcomplex Компилятор С++ генерировал код. Оп ссылался на sqrt и sqrt F7complex. К счастью, это было задокументировано только в разделе Ошибки руководства по С++. Использовавшаяся до 2.0 схема перегрузки взаимодействовала с традиционной схемой компоновки С-програ,мм таким способом, при котором становились очевидными худгпие стороны того и другого. Нам пришлось решать три проблемы: □ отсутствие у компоповпка возможности контролировать типы; □ использование ключевого слова overload; □ связывание фрагментов программы, написанных па С и С++. Первая проблема решается путем добавления к имени каждой функции информации о ее типе. Вторая - отказом от слова overload. Для решения третьей программисту на С++ необходимо яв1ю указать, когда функция должна связываться в стиле С. Поэтому в [Stroustrup, 1988а] я писал: Вопрос в том, можно ли реализовать решение, основанное но этих трех посылках, без заметных затрат и с минимальными неудобствами для программистов но С++. Идеальное решение должно: □ не требовать никаких изменений в С++; □ обеспечить типобезопосную компоновку; iJ допускать простое и удобное связывание с С-функциями; □ не ломать существующий код но С++; □ разрешать использование заголовочных файлов С (в стиле ANSI); □ обеспечивать хорошее обнаружение и диагностику ошибок; □ быть удобным инструментом для построения библиотек; □ не приводить к издержкам во время выполнения, компиляции и компоновки. Ном не удалось найти решение, которое бы строго удовлетворяло всем этим условиям, но принятая схема является неплохим вариантом . Ясно, что во время компоновки потребовалось бы проверять все типы. Но тогда возникал вопрос, как это сделать без написания нового компорговшика для каждой системы. 11.3.2. Реализация компоновки в С++ Имя каждой С++-фупкции кодируется путем добавления к нему типов аргументов. Этим гарантируется, что программа будет скомпонована только в том случае, если у каждой функции есть определергие и типы аргументов в объявлении и определении совпадают. Рассмотрим, например, объявления f(int определяет f Fi f(int i, char* j) {/*...*/ } определяет f FiPc которые М0Ж1Ю правильно обработать: extern f(int); относится к f Fi extern f(int,char*); относится к f FiPc extern f(double,double) ; относится к f Fdd
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |