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

1 ... 12 13 14 [ 15 ] 16 17 18 ... 144


2.8.3. Важность синтаксиса

Я полагаю, что большинство людей уделяет слишком много внимания синтаксису в ущерб вопросам контроля типов. Однако для дизайна C+-I- критичными всегда были проблемы типизации, недвусмысленности и контроля доступа, а вовсе не синтаксиса.

Не хочу сказать, что синтаксис не имеет никакого значения. Напротив, он исключительно важен. Удачный синтаксис помогает программисту при изучении новых концепций и позволяет избегать глупых ошибок, поскольку записать их труднее, чем их правильные альтернативы. Однако синтаксис }1адо проектировать так, чтобы он соответствовал семантическим понятиям языка, а не наоборот. Отсюда следует, что центральной темой при обсуждении языка должен быть вопрос о том, что можно выразить, а не как это сделать.

Тонким нюансом в дискуссиях о совместимости с С было то, что опытные С-програ,ммисты давно свыклись с тем, как та или иная операция осуществлялась раньше, и потому были совершенно нетерпимы к несовместимостям, которые требовались для поддержания такого стиля программирования, который в дизайне С не был заложен. И наоборот, программисты, не писавшие на С, обычно недооценивают значимость синтаксиса для С-программистов.

2.9. Производные классы

Концепция производного класса - это адаптация префиксной нотации классов, принятой в Simula, и уже поэтому она схожа с концепцией подкласса в Smalltalk. Названия производный и базовый были выбраны потому, что я никак не мог запомнить, что есть sub, а что - super, и заметил, что такие сложности возникают не только у меня. Вот еще одно наблюдение: для многих тот факт, что подкласс обычно содержит больше информации, чем его суперкласс, кажется неестественным. Изобретая термины производный и базовый , я отошел от своего принципа не вводить новых названий, если существуют старые. В свою защиту скажу лишь, что я никогда не замечал путаницы по поводу этих терминов и что запомнить их очень просто даже тем, что не имеет математического образования.

В С with Classes для концепции производного класса не было никакой поддержки во время исполнения. В частности, отсутствовало понятие виртуальной функции, которое было в Simula, а позже вошло в C-I-4-. Я просто сомневался (думаю, не без оснований) в своей способности научить людей ими пользоваться и, более того, в способности убедить, что для типичных применений виртуальная функция так же эффективна по скорости и по памяти, как обычная. Многие пользователи, имеющие опыт работы с Simula или Smalltalk, до сих пор в это не верят, пока им не объяснишь подробно, как это сделано в C-i-i-, а некоторые и после испытывают сомнения.

Даже и без виртуальных функций производные классы в С with Classes были полезны для построения новых структур данных из старых и для ассоциирования операций с получающимися типами. Так, с их помощью удалось определить классы связанного списка и задачи (task).



Производные классы

2.9.1. Полиморфизм без виртуальных функций

Даже в отсутствие виртуальных фу}!кций пользователь мог работать с объектами производного класса, а устройство базового класса трактовать как деталь реализации. Напри.мер, если имеется класс вектора с элементами, проиндексированными начиная с О и без проверки выхода за границы диапазона

class vector { /* ... */

int get elem(int i);

to }!a его основе можно построить вектор элементов с индексами из заданного диапазона с контролем выхода за его границы:

class vec : vector {

int hi, lo; public:

/* ... */

new(int lo, int hi); get elem(int i);

int vec.get elem(int i) {

if (i < lo I I hi < i) error( выход за границы диапазона ); return vector.get elem(i - lo) ;

Можно было вместо этого ввести в базовый класс явное поле типа и использовать его совместно с явным приведением типов. Первая стратегия при.менялась, когда пользователь видел только конкретные производные классы, а система видела все их базовые классы. Вторая стратегия полезна тогда, когда базовый класс реализовывал по сути дела одну вариантную запись для множества производных классов.

Например, в работе [Stroustrup, 1982b] приведен следующий уродливый код для извлечения объекта из таблицы и использования имеющегося в нем поля типа:

class elem { /* свойства, хранящиеся в таблице */ }; class table { /* табличные данные и функции поиска */ };

class cl name * cl; /* cl name - производный от elem */ class po name * po; /* po name - производный от elem */ class hashed * table; /* hashed - производный от table */

elem * p = table->look( carrot ) ;

if (P) {

switch (p->type) { /* поле типа type в объектах класса elem */ case PO NAME:

po = (class po name *) p; /* явное преобразование типа */



break; case CL NAME:

cl = (class cl name *) p; /* явное преобразование типа */

break; default:

error( неизвестный тип элемента );

else

error( carrot не определено );

При проектировании С with Classes и С++ было потрачено много усилий ради того, чтобы программисту не приходилось писать такой код.

2.9.2. Контейнерные классы без шаблонов

в то время в моих программах и размышлениях важное место занимал вопрос о том, как с помощью комбинирования базовых классов, явных преобразований типов и (изредка) макросов можно получить обобщенные контейнерные классы. Например, в работе [Stroustrup, 1982b] продемонстрировано, как из списка объектов типа link построить список, который может содержать объекты одного типа:

class wordlink : link {

char word[SIZE]; public:

void clear(void);

class wordlink * get(void);

{ return (class wordlink *) link.getO; }; void put(class wordlink * p) { link.put(p); };

Поскольку каждый объект link, который с помощью функции put () помещается в список, должен принадлежать классу wordlink, то обратное приведение к wordlink объекта типа link, извлекаемого из списка функцией get (), безопасно. Отмечу использование закрытого (по умолчанию, в отсутствие ключевого слова public в спецификации базового класса link; см. раздел 2.10) наследования. Если разрешить использовать wordlink как просто link, то была бы поставлена под уфозу безопасность работы с типами.

Для обеспечения обобщенных типов использовались макросы. Вот как об этом написано в [Stroustrup, 1982b]:

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



1 ... 12 13 14 [ 15 ] 16 17 18 ... 144

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