2.2 Методы, сообщения, классы и объекты

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

delete() должна быть в состоянии определить местонахождение деструктора не зная тип переданного ей объекта. Поэтому пересматривая декларации, приведённые в разделе 2.1, мы должны настоять на том, что указатель, используемый для обнаружения деструктора должен быть в начале всех объектов, передаваемых в delete(), независимо от того, какой тип они имеют.

На что должен указывать этот указатель? Если всё, что мы имеем, это адрес объекта, этот указатель даёт нам доступ к зависимой от типа информации для объекта, такой как функция деструктора. Вполне вероятно, что мы скоро придумаем другие зависимые от типа функции, например, функцию для отображения объектов, или свою функцию сравнения differ(), или функцию clone() для создания полной копии объекта. Поэтому мы будем использовать указатель на таблицу указателей на функции.

Присмотревшись, мы понимаем, что эта таблица должна быть частью описания типа, передаваемого в new(), и очевидное решение — позволить объекту указывать на полное описание типа:
 

struct Class {

    size_t size;

    void * (* ctor) (void * self, va_list * app);

    void * (* dtor) (void * self);

    void * (* clone) (const void * self);

    int (* differ) (const void * self, const void * b);

};

 

struct String {

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

    char * text;

};

 

struct Set {

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

    ...

};

 

Каждый из наших объектов начинается с указателя на описание своего типа и через это описание типа можно найти зависимую от типа информацию для данного объекта: .size размер, который new() выделяет для объекта; .ctor указывает на конструктор, вызываемый new(), который получает выделенную область и остальную часть списка аргументов, изначально переданного new(); .dtor указывает на деструктор, вызываемый delete(), принимающую объект, который будет уничтожен; .clone указывает на функцию копирования, принимающую объект, который надо скопировать; а .differ указывает на функцию, которая сравнивает объект с каким-то другим.

Просматривая этот список, замечаем, что каждая функция работает с объектом, через который она будет выбрана. Только конструктор может иметь дело с частично инициализированной области памяти. Мы называем эти функции методами для таких объектов. Вызов метода называется сообщением и мы помечаем получающий это сообщение объект параметром с именем self. Так как мы используем простые функции C, self не должен быть первым параметром.

Многие объекты будут иметь одинаковый тип дескриптора, то есть они нуждаются в одинаковом объёме памяти и к ним могут быть применены одни и те же методы. Мы называем все объекты с одинаковым типом дескриптора классом; единственный объект называется экземпляром класса. До сих пор класс, абстрактный тип данных, и набор возможных значений вместе с операциями, то есть тип данных, в значительной степени одно и то же.

Объект является экземпляром класса, то есть у него есть состояние, представленное памятью, выделенной new(), и этим состоянием манипулируют с помощью методов данного класса. Условно говоря, объект является значением определённого типа данных.

 

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