Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
c++book1.doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
1.83 Mб
Скачать

1.16 Обобщенные Вектора

"Пока все хорошо," - можете сказать вы, - "но я хочу, чтобы один из этих векторов был типа matrix, который я только что определил." К сожалению, в С++ не предусмотрены средства для определения класса векторов с типом элемента в качестве параметра. Один из способов - продублировать описание и клас­са, и его функций членов. Это не идеальный способ, но зачас­тую вполне приемлемый.

Вы можете воспользоваться препроцессором (#4.7), чтобы механизировать работу. Например, класс vector - упрощенный вариант класса, который можно найти в стандартном заголовоч­ном файле. Вы могли бы написать:

#include <vector.h>

declare(vector,int);

main()

(*

vector(int) vv(10);

vv[2] = 3;

vv[10] = 4; // ошибка: выход за границы

*)

Файл vector.h таким образом определяет макросы, чтобы макрос declare(vector,int) после расширения превращался в описание класса vector, очень похожий на тот, который был оп­ределен выше, а макрос implement(vector,int) расширялся в оп­ределение функций этого класса. Поскольку макрос implement(vector,int) в результате расширения превращается в определение функций, его можно использовать в программе толь­ко один раз, в то время как declare(vector,int) должно ис­пользоваться по одному разу в каждом файле, работающем с этим типом целых векторов.

declare(vector,char);

//...

implement(vector,char);

даст вам отдельный тип "вектор символов". Пример реализации

обобщенных классов с помощью макросов приведен в #7.3.5.

1.17 Полиморфные Вектора

У вас есть другая возможность - определить ваш векторный и другие вмещающие классы через указатели на объекты некото­рого класса:

class common (*

//...

*);

class vector (*

common** v;

//...

public:

cvector(int);

common*& elem(int);

common*& operator[](int);

//...

*);

Заметьте, что поскольку в таких векторах хранятся указа­тели, а не сами объекты, объект может быть "в" нескольких та­ких векторах одновременно. Это очень полезное свойство подобных вмещающих классов, таких, как вектора, связанные списки, множества и т.д. Кроме того, можно присваивать указа­тель на производный класс указателю на его базовый класс, по­этому можно использовать приведенный выше cvector для хране­ния указателей на объекты всех производных от common классов. Например:

class apple : public common (* /*...*/ *)

class orange : public common (* /*...*/ *)

class apple_vector : public cvector (*

public:

cvector fruitbowl(100);

//...

apple aa;

orange oo;

//...

fruitbowl[0] = &aa;

fruitbowl[1] = &oo;

*)

Однако, точный тип объекта, вошедшего в такой вмещающий класс, больше компилятору не известен. Например, в предыдущем примере вы знаете, что элемент вектора является common, но является он apple или orange? Обычно точный тип должен впос­ледствии быть восстановлен, чтобы обеспечить правильное ис­пользование объекта. Для этого нужно или в какой-то форме хранить информацию о типе в самом объекте, или обеспечить, чтобы во вмещающий класс помещались только объекты данного типа. Последнее легко достигается с помощью производного класса. Вы можете, например, создать вектор указателей на apple:

class apple_vector : public cvector (*

public:

apple*& elem(int i)

(* return (apple*&) cvector::elem(i); *)

//...

*);

используя запись приведения к типу (тип)выражение, чтобы преобразовать common*& (ссылку на указатель на common), кото­рую возвращает cvector::elem, в apple*&. Такое применение производных классов создает альтернативу обобщенным классам. Писать его немного труднее (если не использовать макросы та­ким образом, чтобы производные классы фактически реализовыва­ли обобщенные классы, см. #7.3.5), но оно имеет то преиму­щество, что все производные классы совместно используют единственную копию функции базового класса. В случае обобщен­ных классов, таких, как vector(type), для каждого нового ис­пользуемого типа должна создаваться (с помощью implement()) новая копия таких функций. Другой способ, хранение идентифи­кации типа в каждом объекте, приводит нас к стилю программи­рования, который часто называют объекто-основанным или объек­тно-ориентированным.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]