The website "dmilvdv.narod.ru." is not registered with uCoz.
If you are absolutely sure your website must be here,
please contact our Support Team.
If you were searching for something on the Internet and ended up here, try again:

About uCoz web-service

Community

Legal information

8.1 Технические приёмы

8.1 Технические приёмы

Предыдущая  Содержание  Следующая V*D*V

Для обращение к всем объект используется void *. Хотя это упрощает написание кода, навлекается бедствие: манипулирование не-объектом или неправильным объектом в методе, или ещё хуже, выбор метода из описания класса, который его не содержит, вызовет значительные количеств проблем. Давайте проследим вызов динамически компонуемого метода. new() создаёт окружность и к нему применяется селектор draw():

 

void * p = new(Circle, 1, 2, 3);

    draw(p);

 

Селектор доверяет и разыменовывает результат classOf():

 

void draw (const void * _self) {

    const struct PointClass * class = classOf(_self);

 

    assert(class -> draw);

    class -> draw(_self);

}

 

Этот метод селектора доверяет и разыменовывает _self, который изначально является указателем, созданным new():

 

static void Circle_draw (const void * _self) {

    const struct Circle * self = _self;

 

    printf("circle at %d,%d rad %d\n",

    x(self), y(self), self -> rad);

}

 

classOf() также доверяет и разыменовывает указатель. В качестве небольшого утешения производится проверка результата на то, что указатель ненулевой:

 

const void * classOf (const void * _self) {

    const struct Object * self = _self;

 

    assert(self);

 

    assert(self -> class);

    return self -> class;

}

 

В целом каждое присвоение неопределённого значения void * указателю на некоторую структуру подозрительно с самого начала и законность его должна быть проверена. Мы спроектировали наши методы полиморфными, то есть компилятор ANSI-C не может выполнить эту проверку за нас. Мы должны изобрести динамическое средство для проверки типа, которое сильно ограничит ущерб, который может нанести оказавшийся не на своём месте объект или не-объект.

К счастью, наши значения void * знают, на что указывают: они указывают на объекты, которые все занаследованы от Object и поэтому содержат компонент .class, указывающий на их описание класса. Каждое описание класса уникально; таким образом значение указателя в .class может использоваться, чтобы определить, принадлежит ли объект конкретному классу:

 

int isA (const _self, const Class @ class);

int isOf (const _self, const Class @ class);

 

Это два новых статически компонуемых метода для Object и, следовательно, для любого объекта: isA() истина, если объект непосредственно принадлежит определённому классу; isOf() истина, если объект занаследован от указанного класса. Имеют силу следующие аксиомы:

 

isA(0, anyClass)     всегда ложь

isOf(0, anyClass)    всегда ложь

isOf(x, Object)      истина для всех объектов

 

Оказывается, что более полезен ещё один статический метод для Object:

 

void * cast (const Class @ class, const _self);

 

Если isOf(_self, class) истина, cast() возвращает свой аргумент _self, в противном случае cast() завершает работу вызывающего процесса.

cast() теперь собирается заменить assert() в большинстве мест контроля ошибок. Везде, где у нас нет уверенности, мы можем обернуть сомнительный указатель в cast(), чтобы ограничить ущерб, который могло бы нанести неожиданное значение:

 

cast(someClass, someObject);

 

Эта функция также используется для безопасного разыменовывания указателей во время импорта в метод или селектор:

 

struct Circle * self = cast(Circle, _self);

 

Обратите внимание, что параметры cast() имеют естественный порядок для операции по приведению типа: класс написан слева от объекта для приведения. isOf(), однако, берёт те же параметры в противоположном порядке, потому что в операции if мы спросили бы является ли объект "принадлежащим" указанному классу.

Хотя cast() принимает _self с определителем const, он возвращает значение const, чтобы избежать сообщений об ошибках при присвоении. Та же самая игра со словами происходит в стандарте ANSI-C: bsearch() возвращает результат void * для таблицы, переданной как const void *.

 

Предыдущая  Содержание  Следующая