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

1.8 Другая реализация — Bag

1.8 Другая реализация — Bag

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

Можно изменить реализацию без изменения видимого интерфейса в Set.h. На этот раз мы используем динамическую память и представим наборы и объекты в виде структур:

 

struct Set { unsigned count; };

struct Object { unsigned count; struct Set * in; };

 

count отслеживает количество элементов в наборе. Для элемента же count фиксирует сколько раз этот элемент был добавлен в набор. Если count уменьшается каждый раз, когда элемент передаётся в drop(), и удаление элемент происходит только когда count равен нулю, получается Bag, то есть набор, где элементы имеют счётчик ссылок.

Так как для представления наборов и объектов будет использоваться динамическая память, необходимо проинициализировать дескрипторы Set и Object, чтобы new() могла знать, сколько памяти резервировать:

 

static const size_t _Set = sizeof(struct Set);

static const size_t _Object = sizeof(struct Object);

 

const void * Set = & _Set;

const void * Object = & _Object;

 

new() теперь намного проще:

 

void * new (const void * type, ...) {

    const size_t size = * (const size_t *) type;

    void * p = calloc(1, size);

 

    assert(p);

    return p;

}

 

delete() может передать свой аргумент непосредственно в free() — в ANSI-C в free() может быть передан нулевой указатель.

add() вынуждена более или менее доверять аргументам своего указателя. Она увеличивает счётчик ссылок элемента и количество элементов в наборе:

 

void * add (void * _set, const void * _element) {

    struct Set * set = _set;

    struct Object * element = (void *) _element;

 

    assert(set);

    assert(element);

 

    if (! element -> in)

        element -> in = set;

    else

        assert(element -> in == set);

    ++ element -> count, ++ set -> count;

 

    return element;

}

 

find() по-прежнему проверяет, указывает ли элемент на соответствующий набор:

 

void * find (const void * _set, const void * _element) {

    const struct Object * element = _element;

 

    assert(_set);

    assert(element);

 

    return element -> in == _set ? (void *) element : 0;

}

 

contains() опирается на find() и остаётся неизменной.

Если drop() находит данный элемент в наборе, она уменьшает счётчик ссылок элемента и количество элементов в наборе. Если счётчик ссылок достигает нуля, элемент удаляется из набора:

 

void * drop (void * _set, const void * _element) {

    struct Set * set = _set;

    struct Object * element = find(set, _element);

    if (element) {

        if (-- element -> count == 0)

            element -> in = 0;

        -- set -> count;

    }

    return element;

}

 

Теперь мы можем предоставить новую функцию count(), которая возвращает количество элементов в наборе:

 

unsigned count (const void * _set) {

    const struct Set * set = _set;

 

    assert(set);

    return set -> count;

}

 

Конечно, было бы проще позволить приложению считывать компонент .count непосредственно, но мы настаиваем на нераскрытии представления наборов. Накладные расходы на вызов функции незначительны по сравнению с опасностью того, что приложение будет способно перезаписать критическое значение.

Bag-и (множества) ведут себя иначе, чем set-ы (наборы): элемент может быть добавлен несколько раз; он исчезнет из множества только после того, как будет удалён столько раз, сколько был добавлен. Наше приложение в разделе 1.6 добавило объект a в набор дважды. После того, как он удаляется из набора один раз, contains() по-прежнему найдёт его в этом множестве. Тестовая программа теперь выводит

 

ok

drop?

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