Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Cpp_Страуструп.doc
Скачиваний:
16
Добавлен:
03.05.2015
Размер:
3.2 Mб
Скачать

5.4 Еще о классах

В этом разделе описаны дополнительные свойства класса. Описан

способ обеспечить доступ к частным членам в функциях, не являющихся

членами ($$5.4.1). Описано, как разрешить коллизии имен членов

($$5.4.2) и как сделать описания классов вложенными ($$5.4.3), но

при этом избежать нежелательной вложенности ($$5.4.4). Вводится понятие

статических членов (static), которые используются для представления

операций и данных, относящихся к самому классу, а не к отдельным

его объектам ($$5.4.5). Раздел завершается примером, показывающим,

как можно построить дискриминирующее (надежное) объединение ($$5.4.6).

5.4.1 Друзья

Пусть определены два класса: vector (вектор) и matrix (матрица).

Каждый из них скрывает свое представление, но дает полный набор операций

для работы с объектами его типа. Допустим, надо определить функцию,

умножающую матрицу на вектор. Для простоты предположим, что

вектор имеет четыре элемента с индексами от 0 до 3, а в матрице

четыре вектора тоже с индексами от 0 до 3. Доступ к элементам

вектора обеспечивается функцией elem(), и аналогичная функция есть

для матрицы. Можно определить глобальную функцию multiply

(умножить) следующим образом:

vector multiply(const matrix& m, const vector& v);

{

vector r;

for (int i = 0; i<3; i++) { // r[i] = m[i] * v;

r.elem(i) = 0;

for (int j = 0; j<3; j++)

r.elem(i) +=m.elem(i,j) * v.elem(j);

}

return r;

}

Это вполне естественное решение, но оно может оказаться очень

неэффективным. При каждом вызове multiply() функция elem() будет

вызываться 4*(1+4*3) раз. Если в elem() проводится настоящий

контроль границ массива, то на такой контроль будет потрачено

значительно больше времени, чем на выполнение самой функции, и в

результате она окажется непригодной для пользователей. С другой

стороны, если elem() есть некий специальный вариант доступа без

контроля, то тем самым мы засоряем интерфейс с вектором и матрицей

особой функцией доступа, которая нужна только для обхода контроля.

Если можно было бы сделать multiply членом обоих классов

vector и matrix, мы могли бы обойтись без контроля индекса при

обращении к элементу матрицы, но в то же время не вводить специальной

функции elem(). Однако, функция не может быть членом двух классов.

Надо иметь в языке возможность предоставлять функции, не являющейся

членом, право доступа к частным членам класса. Функция - не член

класса, - имеющая доступ к его закрытой части, называется другом

этого класса. Функция может стать другом класса, если в его

описании она описана как friend (друг). Например:

class matrix;

class vector {

float v[4];

// ...

friend vector multiply(const matrix&, const vector&);

};

class matrix {

vector v[4];

// ...

friend vector multiply(const matrix&, const vector&);

};

Функция-друг не имеет никаких особенностей, за исключением права

доступа к закрытой части класса. В частности, в такой функции

нельзя использовать указатель this, если только она действительно

не является членом класса. Описание friend является настоящим

описанием. Оно вводит имя функции в область видимости класса,

в котором она была описана, и при этом происходят обычные проверки

на наличие других описаний такого же имени в этой области

видимости. Описание friend может находится как в общей, так и в

частной частях класса, это не имеет значения.

Теперь можно написать функцию multiply, используя элементы

вектора и матрицы непосредственно:

vector multiply(const matrix& m, const vector& v)

{

vector r;

for (int i = 0; i<3; i++) { // r[i] = m[i] * v;

r.v[i] = 0;

for ( int j = 0; j<3; j++)

r.v[i] +=m.v[i][j] * v.v[j];

}

return r;

}

Отметим, что подобно функции-члену дружественная функция

явно описывается в описании класса, с которым дружит. Поэтому она

является неотъемлемой частью интерфейса класса наравне с

функцией-членом.

Функция-член одного класса может быть другом другого класса:

class x {

// ...

void f();

};

class y {

// ...

friend void x::f();

};

Вполне возможно, что все функции одного класса являются друзьями

другого класса. Для этого есть краткая форма записи:

class x {

friend class y;

// ...

};

В результате такого описания все функции-члены y становятся друзьями

класса x.