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

1 ... 70 71 72 [ 73 ] 74 75 76 ... 144


В с принято, что символьный литерал (а) имеет тип int. Как пи странно, приписывание а типа char в С++ не приводит к проблемам сов.местимости. Если не считать примера sizeof ( а ), то любая конструкция, которая может быть записана в С и С++, приводит к одному и тому же результату.

При }газ1гачепии символьному литералу типа char я отчасти опирался па сообщение Майка Тимана об опыте использования в GNU С++ флага компилятора, обеспечиваюп1его такую интерпретацию.

С другой стороны, выяснилось, что разницу .между const и ne-const можно успешно использовать. Наглядным при.мером такой перегрузки является пара функций

char* strtok(char*, const char*);

const char* strtok(const char*, const char*);

в качестве альтернативы стандартной функции из библиотеки ANSI С

char* strtok(const char*, const char*);

Функция strtok () из библиотеки С возвращает подстроку константной строки, переданной в качестве первого аргумента. Описать данную подстроку без квалификатора const в С++ невозможно, поскольку это расценивается как неявное 1гарушение системы типов. С другой стороны, песов.местимость с С должна быть сведена к миии.муму, поэто.му предоставлергие двух вариантов функции strtok представляется наиболее разу.мны.\г вариантом.

Допущение перегрузки па осргове на/гичия илц отсутствия const - часть общей стратегии ужесточения правил употребления const (см. раздел 13.3).

Опыт показал, что при сопоставлении функций следует принимать во внимание иерархии, образованные в результате открытого наследования. При наличии выбора следует отдавать предночтергие преобразованию в самый низший в иерархии класс. Аргу.мент void* выбирается лишь в то.м случае, если никакой другой указатель не подходит. Например:

class В {/*...*/ };

class ВВ : public В { /* ... */ };

class ВВВ : public ВВ { /* ... */ };

void f(В*); void f(ВЗ*); void f(void*);

void g(BBB* pbbb, BB* pbb, B* pb, int* pi) {

f(pbbb); f(BB*)

f(pbb); f(BB*)

f(pb); f(B*)

f(pi); lit (void*)

abs(l.OF); abs(float) abs(a); abs(char)



Данное правило разрешения неоднозначности соответствует правилу для вызова виртуальных функций, в соответствии с которым выбирается член самого нижнего в иерархии класса. Изменение было очевидным. В результате исчезли опшбки.

Правда, у этого правила есть одно интересное свойство. Оно устанавливает void* в качестве корня дерева преобразовании классов. Это согласуется с представлением о том, что конструктор создает объект в неформатированной памяти, а деструктор преврашает объект в нее (см. ра,зделы 2.11.1 и 10.2). Преобразова1ше вида В* в void* позволяет рассматривать объект как неформатированную память, если никакие другие его свойства не представляют шгтереса.

11.2.2. Управление неоднозначностью

в первоначальном механиз.ме перегрузки разрепшние неоднозначностей зависело от порядка объявлергий. Объявления рассматривались поочередно, и предпочтение отдавалось тому, которое встретилось в тексте раньше. Чтобы и,збежать недоразумений, при сопоставлении принимались во внимание только преобразования, не сужающие тип. Напри.мер:

overload void print(int); первоначальные (до 2.0) правила: void print(double);

void g() {

print(2.0); print(double): print(2.0)

преобразование double->int не рассматривается

print(2.OF); print(double): print(double(2.OF))

преобразование float->int не рассматривается преобразование float->double рассматривается

print(2); print(int): print(2)

Это правило легко формулируется, эффективно ко.мпилируется, доступно для понимания, тривиально реализуется в ко.мпиляторе, но является иостояш1ы.м источником ошибок. Изменение порядка объявлений на обратный полностью mciw-ет смысл кода:

overload void print(double); первоначальные правила: void print(int);

void g() {

print(2.0); print(double): print(2.0)

print(2.OF); print(double): print(double(2.OF))

преобразование float->double

рассматривается



Таким образом, зависимость от порядка чревата ошибками. Это стало серьезным препятствием на пути эволюции С-и- в направлении более интенсивного использования библиотек. Моей цели - распространить взгляд на программирование как на составление программ из независимо разработанных кусков (см. также раздел 11.3) - наряду со многими другими факторами препятствовала и зависимость от порядка.

Независимые от порядка правила перегрузки весьма усложняют определение и реализацию С-и-, поскольку следует поддерживать совместимость как с С, так и с предыдушим вариантом С-и-. В частности, не годилось простое правило: Выражение, которое может быть корректно интерпретировано хотя бы двумя разными способами, неоднозначно и, стало быть, незаконно . Если принять данный тезис, то незаконными будут все вызовы print (), приведенные в примере выше.

Я решил, что требуется некое правило лучшего соответствия , которое позволило бы предпочесть точное соответствие типов соответствию, требующему преобразований, а безопасные преобразования (типа float в double) - небезопасным (сужающим тип, искажающим значение и т.д., например, float в int). В результате обсуждения, уточнения и ревизии продолжались несколько лет. Некоторые детали все еще обсуждаются комитетом по стандартизации. Вместе со мной наиболее активное участие в процессе принимали Дуг Макилрой, Энди Кениг, Джонатан Шопиро. Еще на ранней стадии Дуг заметил, что мы чересчур близко подошли к попытке спроектировать естественную систему для неявных преобразований. Он считал правила PL/I (которые помогал проектировать сам) доказательством того, что такую естественную систему нельзя создать для богатого набора типов данных - а в C-t-t- есть весьма богатый набор встроенных типов с нерегулируемыми правилами преобразований, а также возможность определять преобразования между произвольными типами, определенными пользователем. Желание сохранить совместимость с С, ожидания пользователей, намерение разрешить им определять типы, с которыми можно было бы работать точно так же, как с встроенными, - все это не позволяло запретить неявные преобразования. Полагаю, что решение оставить неявные преобразования было правильным. Я также согласен с наблюдением Дуга, что задача свести к минимуму количество неприятностей, которые могут преподнести неявные преобразования, сложна по существу. Согласен я и с тем, что полностью их избежать не удастся (по крайней мере, при соблюдении требования о совместимости с С). Просто у программистов разные ожидания, поэтому, какое правило ни выбери, для кого-то оно обернется неприятным сюрпризом.

Фундаментальная проблема заключается в том, что граф неявных преобразований встроенных типов содержит циклы. Так, существует неявное преобразование не только из char в int, но и из int в char. Это потенциальный источник бесконечного числа мелких ошибок. Поэтому было решено отказаться от схемы, основанной на графе преобразований. Вместо нее мы изобрели систему соответствий

print(2); print(double): print(double(2)) преобразование int->double рассматривается



1 ... 70 71 72 [ 73 ] 74 75 76 ... 144

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