Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
лекции / Лекция 3-4n.docx
Скачиваний:
0
Добавлен:
11.02.2026
Размер:
74.69 Кб
Скачать

Дружественные методы

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

Их объявление аналогично объявлениям обычных дружественных функций, за исключением имени метода с имяКласса:: префиксом в начале (например, Display::displayItem()).

Переделаем наш предыдущий пример, чтобы метод Display::displayItem () был дружественным классу Values.

Мы могли бы сделать следующее:

class Display; // предварительное объявление класса Display

class Values {

private:

int m_intValue;

double m_dValue;

public:

Values (int intValue, double dValue) {

m_intValue = intValue;

m_dValue = dValue;

}

// Делаем метод Display::displayItem () другом класса Values

friend void Display::displayItem(Values& value); // ошибка: Values //не видит полного определения класса Display

};

class Display {

private:

bool m_displayIntFirst;

public:

Display (bool displayIntFirst) {m_displayIntFirst = displayIntFirst;}

void displayItem (Values &value) {

if (m_displayIntFirst)

std::cout << value.m_intValue << " " << value.m_dValue << '\n';

else // или выводим сначала double

std::cout << value.m_dValue << " " << value.m_intValue << '\n';

}

};

Однако это не сработает!

Чтобы сделать метод дружественным классу, компилятор должен увидеть полное определение класса, в котором дружественный метод определяется (а не только лишь его прототип).

Поскольку компилятор, прочёсывая последовательно строчки кода не увидел полного определения класса Display, но успел увидеть прототип его метода, то он выдаст ошибку в строке определения этого метода дружественным классу Values.

Можно попытаться переместить определение класса Display выше определения класса Values:

class Display {

private:

bool m_displayIntFirst;

public:

Display (bool displayIntFirst) {m_displayIntFirst = displayIntFirst;} {

void displayItem (Values &value) // ошибка: Компилятор не знает, //что такое Values

If (m_displayIntFirst)

std::cout << value.m_intValue << " " << value.m_dValue << '\n';

else // или выводим сначала double

std::cout << value.m_dValue << " " << value.m_intValue << '\n';

}

};

class Values {

private:

Int m_intValue;

double m_dValue;

public:

Values (int intValue, double dValue) {

m_intValue = intValue;

m_dValue = dValue;

}

// Делаем метод Display::displayItem() другом класса Values

friend void Display::displayItem(Values& value);

};

Однако теперь мы имеем другую проблему. Поскольку метод Display::displayItem() использует ссылку на объект класса Values в качестве параметра, а мы только что перенесли определение Display выше определения Values, то компилятор будет жаловаться, что он не знает, что такое Values. Получается замкнутый круг.

Это также можно очень легко решить:

   Во-первых, для класса Values используем предварительное объявление.

    Во-вторых, переносим определение метода Display::displayItem() за пределы класса Display и размещаем его после полного определения класса Values.

Вот как это будет выглядеть:

#include <iostream>

class Values; // предварительное объявление класса Values

class Display {

private:

bool m_displayIntFirst;

public:

Display (bool displayIntFirst) { m_displayIntFirst = displayIntFirst;

}

void displayItem (Values &value); // предварительное объявление, //приведенное выше, требуется для этой строки

};

class Values {

// полное определение класса Values

private:

int m_intValue;

double m_dValue;

public:

Values (int intValue, double dValue) {

m_intValue = intValue;

m_dValue = dValue;

}

// Делаем метод Display::displayItem () другом класса Values

friend void Display::displayItem (Values& value);

};

// Теперь мы можем определить метод Display::displayItem (), которому //требуется увидеть полное определение класса Values

void Display::displayItem (Values &value) {

if (m_displayIntFirst)

std::cout << value.m_intValue << " " << value.m_dValue << '\n';

else // или выводим сначала double

std::cout << value.m_dValue << " " << value.m_intValue << '\n';

}

int main () {

Values value (7, 8.4);

Display display(false);

display.displayItem (value);

return 0;

}

Теперь всё будет работать правильно. Хотя это может показаться несколько сложным, но перемещение классов и методов нужно только потому, что мы пытаемся сделать всё в одном файле.

Лучшим решением было бы поместить каждое определение класса в отдельный заголовочный файл с определениями методов в соответствующих файлах .cpp. (детально об этом здесь).

Таким образом, все определения классов стали бы видны сразу во всех файлах .cpp, и никаких действий с перемещениями не понадобилось бы!

Заключение

Дружественная функция/класс — это функция/класс, которая имеет доступ к закрытым членам другого класса, как если бы она сама была членом этого класса. Это позволяет функции/классу работать в тесном контакте с другим классом, не заставляя другой класс делать открытыми свои закрытые члены.

Тест

Точка в геометрии — это позиция в пространстве.

Мы можем определить точку в 3D-пространстве как набор координат x, y и z.

Например, Point (0.0, 1.0, 2.0) будет точкой в координатном пространстве x = 0.0, y = 1.0 и z = 2.0.

Вектор в физике — это величина, которая имеет длину и направление (но не положение).

Мы можем определить вектор в 3D-пространстве через значения x, y и z, представляющие направление вектора вдоль осей x, y и z. Например, Vector (1.0, 0.0, 0.0) будет вектором, представляющим направление только вдоль положительной оси x длиной 1.0.

Вектор может применятся к точке для перемещения точки на новую позицию. Это делается путем добавления направления вектора к позиции точки. Например, Point (0.0, 1.0, 2.0) + Vector (0.0, 2.0, 0.0) даст точку (0.0, 3.0, 2.0).

Точки и векторы часто используются в компьютерной графике (точка для представления вершин фигуры, а векторы — для перемещения фигуры).

Исходя из следующей программы:

#include <iostream>

class Vector3D {

private:

double m_x, m_y, m_z;

public:

Vector3D (double x = 0.0, double y = 0.0, double z = 0.0)

: m_x(x), m_y(y), m_z(z) {}

void print () {

std::cout << "Vector(" << m_x << " , " << m_y << " , " << m_z << ")\n";

}

};

class Point3D {

private:

double m_x, m_y, m_z;

public:

Point3D(double x = 0.0, double y = 0.0, double z = 0.0)

: m_x(x), m_y(y), m_z(z) {}

void print() {

std::cout << "Point(" << m_x << " , " << m_y << " , " << m_z << ")\n";

}

void moveByVector (const Vector3D &v) {

// Реализуйте эту функцию как дружественную классу Vector3D

}

};

int main () {

Point3D p (3.0, 4.0, 5.0);

Vector3D v(3.0, 3.0, -2.0);

p.print();

p.moveByVector(v);

p.print();

return 0;

}

a) Сделайте класс Point3D дружественным классу Vector3D и реализуйте метод moveByVector () в классе Point3D.

b) Вместо того, чтобы класс Point3D был дружественным классу Vector3D, сделайте метод Point3D::moveByVector() дружественным классу Vector3D.

c) Переделайте свой ответ из задания b, используя 5 отдельных файлов: Point3D.h, Point3D.cpp, Vector3D.h, Vector3D.cpp и main.cpp.