>#include "namedPoint.h"

>#include


>int main(int ac, char** av) {

> struct NamedPoint* origin = makeNamedPoint(0.0, 0.0, "origin");

> struct NamedPoint* upperRight = makeNamedPoint

> (1.0, 1.0, "upperRight");

> printf("distance=%f\n",

> distance(

> (struct Point*) origin,

> (struct Point*) upperRight));

>}


Внимательно рассмотрев основной код в файле >main.c, можно заметить, что структура данных >NamedPoint используется, как если бы она была производной от структуры >Point. Такое оказалось возможным потому, что первые два поля в >NamedPoint совпадают с полями в >Point. Проще говоря, >NamedPoint может маскироваться под >Point, потому что >NamedPoint фактически является надмножеством >Point и имеет члены, соответствующие структуре >Point, следующие в том же порядке.

Этот прием широко применялся[15] программистами до появления ОО. Фактически именно так C++ реализует единственное наследование.

То есть можно сказать, что некоторая разновидность наследования у нас имелась задолго до появления языков ОО. Впрочем, это утверждение не совсем истинно. У нас имелся трюк, хитрость, не настолько удобный, как настоящее наследование. Кроме того, с помощью описанного приема очень сложно получить что-то похожее на множественное наследование.

Обратите также внимание, как в >main.c мне пришлось приводить аргументы >NamedPoint к типу >Point. В настоящем языке ОО такое приведение к родительскому типу производится неявно.

Справедливости ради следует отметить, что языки ОО действительно сделали маскировку структур данных более удобной, хотя это и не совсем новая особенность.

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

Но у нас есть еще одно понятие.

Полиморфизм?

Была ли возможность реализовать полиморфное поведение до появления языков ОО? Конечно! Взгляните на следующую простую программу copy на языке C.


>#include


>void copy() {

> int c;

> while ((c=getchar())!= EOF)

> putchar(c);

>}


Функция >getchar() читает символы из >STDIN. Но какое устройство в действительности скрыто за ширмой >STDIN? Функция >putchar() записывает символы в устройство >STDOUT. Но что это за устройство? Эти функции являются полиморфными – их поведение зависит от типов устройств >STDIN и >STDOUT.

В некотором смысле >STDIN и >STDOUT похожи на интерфейсы в силе Java, когда для каждого устройства имеется своя реализация этих интерфейсов. Конечно, в примере программы на C нет никаких интерфейсов, но как тогда вызов >getchar() передается драйверу устройства, который фактически читает символ?

Ответ на этот вопрос прост: операционная система UNIX требует, чтобы каждый драйвер устройства ввода/вывода реализовал пять стандартных функций[16]: >open, >close, >read, >write и >seek. Сигнатуры этих функций должны совпадать для всех драйверов.

Структура FILE имеет пять указателей на функции. В нашем случае она могла бы выглядеть как-то так:


>struct FILE {

> void (*open)(char* name, int mode);

> void (*close)();

> int (*read)();

> void (*write)(char);

> void (*seek)(long index, int mode);

>};


Драйвер консоли определяет эти функции и инициализирует указатели на них в структуре FILE примерно так:


>#include "file.h"


>void open(char* name, int mode) {/*…*/}

>void close() {/*…*/};

>int read() {int c;/*…*/ return c;}

>void write(char c) {/*…*/}

>void seek(long index, int mode) {/*…*/}


>struct FILE console = {open, close, read, write, seek};


Если теперь предположить, что символ >STDIN определен как указатель >FILE* и ссылается на структуру console, тогда