Язык программирования C++ от Страуструпа

Друзья


Пусть определены два класса: 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.


Содержание раздела