Программирование >>  Полиморфизм без виртуальных функций в с++ 

1 ... 72 73 74 [ 75 ] 76 77 78 ... 144


После очередной перепалки в 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



1 ... 72 73 74 [ 75 ] 76 77 78 ... 144

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика