2.5 Реализация — String

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

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

Конструктор извлекает текст, переданный в new(), и сохраняет динамическую копию в struct String, память для которой была выделена new():

 

struct String {

    const void * class; /* должен быть первым */

    char * text;

};

 

static void * String_ctor (void * _self, va_list * app) {

    struct String * self = _self;

    const char * text = va_arg(* app, const char *);

 

    self -> text = malloc(strlen(text) + 1);

    assert(self -> text);

    strcpy(self -> text, text);

    return self;

}

 

В конструкторе необходимо лишь проинициализировать .text, так как .class уже установлен new().

Деструктор освобождает динамическую память, контролируемую строкой. Так как delete() может вызвать деструктор только если self не является нулевым, о проверках заботиться не надо:

 

static void * String_dtor (void * _self) {

    struct String * self = _self;

 

    free(self -> text), self -> text = 0;

    return self;

}

 

String_clone() создаёт копию строки. Позже обе, оригинал и копия, будут переданы в delete(), поэтому мы должны создать новую динамическую копию текста строки. Это можно легко сделать вызовом new():

 

static void * String_clone (const void * _self) {

    const struct String * self = _self;

 

    return new(String, self -> text);

}

 

String_differ() конечно же возвращает ложь, если мы смотрим на одинаковые строковые объекты, и истину, если сравниваем строку с совершенно другим объектом. Если мы действительно сравниваем две разные строки, то проверку выполняем с помощью strcmp():

 

static int String_differ (const void * _self, const void * _b) {

    const struct String * self = _self;

    const struct String * b = _b;

 

    if (self == b)

        return 0;

    if (! b || b -> class != String)

        return 1;

    return strcmp(self -> text, b -> text);

}

 

Дескрипторы типов являются уникальными — здесь мы используем этот факт, чтобы выяснить, действительно ли наш второй аргумент является строкой.

Все эти методы являются статическими, потому что они должны вызываться только через new(), delete() или селекторы. Эти методы сделаны доступными для селекторов с помощью дескриптора данного типа:

 

#include "new.r"

 

static const struct Class _String = {

    sizeof(struct String),

    String_ctor, String_dtor,

    String_clone, String_differ

};

 

const void * String = & _String;

 

String.c подключает публичные декларации, описанные в String.h и new.h. Для того, чтобы правильно проинициализировать дескриптор типа, он также подключает заголовок new.r, который содержит определение для представления struct Class, показанного в разделе 2.2.

 

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