class vec: public vector (* int low, high; public: vec(int,int);

int amp; elem(int); int amp; operator[](int); *);

Определение vec как :public vector

означает, в первую очередь, что vec – это vector. То есть, тип vec имеет (наследует) все свойства типа vector дополнительно к тем, что описаны специально для него. Говорят, что vector является базовым классом для vec, а о vec говорится, что он производный класс от vector. Класс vec модифицирует класс vector тем, что в нем задается другой конструктор, который требует от пользователя указывать две границы изменения индекса, а не длину, и имеются свои собственные функции доступа elem(int) и operator[](int). Функция elem() класса vec легко выражается через elem() класса vector: int amp; vec::elem(int i) (* return vector::elem(i-low); *)

Операция разрешения области видимости :: используется для того, чтобы не было бесконечной рекурсии обращения к vec::elem() из нее самой. с помощью унарной операции :: можно ссылаться на нелокальные имена. Было бы разумно описать vec:: elem() как inline, поскольку, скорее всего, эффективность существенна, но необязательно, неразумно и невозможно написать ее так, чтобы она непосредственно использовала закрытый член v класса vector. Функции производного класса не имеют специального доступа к закрытым членам его базового класса.

Конструктор можно написать так:

vec::vec(int lb, int hb) : (hb-lb+1) (* if (hb-lb«0) hb = lb; low = lb; high = hb; *)

Запись: (hb-lb+1) используется для определения списка параметров конструктора базового класса vector::vector(). Этот конструктор вызывается перед телом vec::vec(). Вот небольшой пример, который можно запустить, если скомпилировать его вместе с остальными описаниями vector:

#include «streams.h»

void error(char* p) (* cerr «„ p «« « “; // cerr – выходной поток сообщений об ошибках exit(1); *)

void vector::set_size(int) (* /* пустышка */ *)

int amp; vec::operator[](int i) (* if (i«low !! high„i) error(«vec index out of range“); // индекс vec за границами return elem(i); *)

main() (* vector a(10); for (int i=0; i«a.size(); i++) (* a[i] = i; cout „„ a[i] «« ' '; *) cout «« « “; vec b(10,19); for (i=0; i«b.size(); i++) b[i+10] = a[i]; for (i=0; i«b.size(); i++) cout «« b[i+10] «« ' '; cout «« « “; *)

Он выдает 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9

Это направление развития векторного типа можно разрабатывать дальше. Довольно просто сделать многомерные массивы, массивы, в которых число размерностей задается как параметр конструктора, массивы в стиле Фортрана, к которым можно одновременно обращаться и как к имеющим две размерности, и как к имеющим три, и т.д.

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

1.14 Еще об операциях

Другое направление развития – снабдить вектора операциями:

class Vec : public vector (* public: Vec(int s) : (s) (**) Vec(Vec amp;); ~Vec() (**) void operator=(Vec amp;); void operator*=(Vec amp;); void operator*=(int); //... *);

Обратите внимание на способ определения конструктора производного класса, Vec::Vec(), когда он передает свой параметр конструктору базового класса vector::vector() и больше не делает ничего. Это полезная парадигма. Операция присваивания перегружена, ее можно определить так:

void Vec::operator=(Vec amp; a) (* int s = size(); if (s!=a.size()) error(«bad vector size for =»); // плохой размер вектора для = for (int i = 0; i«s; i++) elem(i) = a.elem(i); *)

Присваивание объектов класса Vec теперь действительно копирует элементы, в то время как присваивание объектов

vector просто копирует структуру, управляющую доступом к элментам. Последнее, однако, происходит и тогда, когда vector копируется без явного использования операции присваивания: (1) когда vector передается как параметр и (3) когда vector передается как значение, возвращаемое функцией. Чтобы обрабатывать эти случаи для векторов Vec, вы определяете конструктор Vec(Vec amp;): Vec::Vec(Vec amp; a) : (a.size()) (* int sz = a.size(); for (int i = 0; i«sz; i++) elem(i) = a.elem(i); *) Этот конструктор инициализирует Vec как копию другого Vec, и будет вызываться в отмеченных выше случаях. Выражение в левой части таких операций, как = и +=, безусловно определено, поэтому кажется вполне естественным реализовать их как операции над объектом, который обозначается (денотируется) этим выражением. В частности, тогда они смогут изменять значение своего первого операнда. Левый операнд таких операций, как + и – не требует особого внимания. Вы могли бы, например, передавать оба аргумента по значению и все рано получить правильную реализацию векторного сложения. Однако вектора могут оказаться большими, поэтому чтобы избежать ненужного копирования операнды операции + передаются в operator +() по ссылке:

Vec operator+(Vec amp; a,Vec amp;b) (* int s = a.size(); if (s != b.size()) error(«bad vector size for +»); // плохой размер вектора для + Vec sum(s); for (int i=0; i«s; i++) sum.elem(i) = a.elem(i) + b.elem(i); return sum; *)

Вот пример небольшой программы, которую можно выполнить, если скомпилировать ее вместе с ранее приведенными описаниями vector:

#include «stream.h»

void error(char* p) (* cerr «„ p «« « “; exit(1); *)

void vector::set_size(int) (* /*...*/ *)

int amp; vec::operator[](int i) (* /*...*/ *)

main() (* Vec a(10); Vec b(10); for (int i=0; i«a.size(); i++) a[i] = i; b = a; Vec c = a+b; for (i=0; i„c.size(); i++) cout «« c[i] «« « “; *)

1.15 Друзья (friend)

Функция operator+() не воздействует непосредственно на представление вектора. Действительно, она не может этого делать, поскольку не является членом. Однако иногда желательно дать функциям не членам возможность доступа к закрытой части класса. Например, если бы не было функции «доступа без проверки» vector::elem(), вам пришлось бы проверять индекс i на соответствие границам три раза за каждый проход цикла. Здесь мы избежали этой сложности, но она довольно типична, поэтому у класса есть механизм предоставления права доступа к своей закрытой части функциям не членам. Просто в класс помещается описание функции, перед которым стоит ключевое слово friend. Например, если имеется

class Vec; // Vec – имя класса class vector (* friend Vec operator+(Vec, Vec); //... *);

То вы можете написать Vec operator+(Vec a, Vec b) (* int s = a.size(); if (s != b.size()) error(«bad vector size for +»); // плохой размер вектора для + Vec amp; sum = *new Vec(s); int* sp = sum.v; int* ap = a.v; int* bp = b.v; while (s–) *sp++ = *ap++ + *bp++; return sum; *)

Одним из особенно полезных аспектов механизма friend является то, что функция может быть другом двух и более классов. Чтобы увидеть это, рассмотрим определение vector и matrix, а затем определение функции умножения (см. #с.8.8).

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

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

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

#include «vector.h»

Вы читаете C++
Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату