Лекции.Орг


Поиск:




Категории:

Астрономия
Биология
География
Другие языки
Интернет
Информатика
История
Культура
Литература
Логика
Математика
Медицина
Механика
Охрана труда
Педагогика
Политика
Право
Психология
Религия
Риторика
Социология
Спорт
Строительство
Технология
Транспорт
Физика
Философия
Финансы
Химия
Экология
Экономика
Электроника

 

 

 

 


Дружественные функции и классы

Лекция №6

Функции, дружественные одному классу

C++ предоставляет возможность обойти (или нарушить) один из основополагающих принципов ООП – принцип инкапсуляции – с помощью друзей. Однако без веских причин ее лучше не использовать. С++ позволяет объявлять два вида друзей класса: дружественную функцию или дружественный класс.

Обычный способ доступа к закрытым членам класса – использование открытой функции-члена. Однако C++ поддерживает и другой способ получения доступа к закрытым членам класса – с помощью дружественных функций. Дружественные функции не являются членами класса, но имеют доступ к его закрытым членам. Более того, одна такая функция может иметь доступ к закрытым членам нескольких классов.

Чтобы объявить функцию дружественной некоторому классу, в определение этого класса включают ее прототип, перед которым ставится ключевое слово friend.

Пример 1.

class Any

{

int n, d;

public:

Any (int p, int r) { n = p; d = r;}

friend bool isDel (Any s);

}

bool isDel (Any s)

{

if (!(s. n % s. d)) return true;

return false;

// return (s. n % s. d)? false: true;

}

int main ()

{

Any ob (12, 3);

if (isDel (ob)) cout <<"Yes"<< endl;

else cout <<"No"<< endl;

}

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

Пример 2.

Файл Dot. h

class Dot // класс точки

{

const char name; // имя точки

double x, y; // координаты точки

public:

Dot( char Name): name (Name) { x =0; y =0;}

Dot( char Name, double X, double Y): name (Name) { x = X; y = Y;}

inline double GetX () const {return x;}

inline double GetY () const {return y;}

inline void SetX (double X) { x = X;}

inline void SetY (double Y) { y = Y;}

double Dist (Dot B) const;

friend double Dist (const Dot & A, const Dot & B);

};

double Dist (Dot * pA, Dot * pB); // функция получает указатели на точки

double Area( const Dot & A, const Dot & B, const Dot & C);

Файл Dot. cpp

double Dot:: Dist (Dot B) const

{

double X = B. xx;

double Y = B. yy;

return sqrt (X * X + Y * Y);

}

double Dot:: Dist (const Dot & A, const Dot & B)

{

double X = A. xB. x;

double Y = A. yB. y;

return sqrt (X * X + Y * Y);

}

double Dist (Dot * pA, Dot * pB)

{

double X = pA -> GetX ()− pB -> GetX (); // объявляет и вычисляет

double Y =* pA.GetY ()−* pB.GetY (); // катеты прямоугольного треугольника

return sqrt (X * X + Y * Y); // вычисляет и возвращает значение

} // гипотенузы прямоугольного треугольника

double Area (const Dot & A, const Dot & B, const Dot & C)

{

double a = Dist (B, C);

double b = Dist (A, C);

double c = Dist (A, B);

double p =(a + b + c)/2.0;

return sqrt (p *(pa)*(pb)*(pc));

}

Файл Main. cpp

int main ()

{

char S [30];

Dot A ('A', 3, 4), B ('B', −3, 4);

Dot C ('C');

CharToOem ("Длина отрезка ", S);

cout << S <<"AB="<< A. Dist(B)<<'\n';

cout << S <<"BC="<< Dist (B, C)<<'\n';

cout << S <<"AC="<< Dist (& A, & C)<<'\n';

CharToOem ("Площадь треугольника ", S);

cout << S <<"ABC="<< Area (A, B, C)<<'\n';

}

В приведённом примере объявлен класс точки Dot и решается задача вычисления расстояния между двумя точками. Задача решена тремя различными способами.

Функция double Dot:: Dist(Dot B) const является членом класса Dot и возвращает значение расстояния между текущей и заданной точками. Спецификатор const указывает компилятору, что состояние текущего объекта не должно изменяться. В качестве параметра функция получает целиком объект типа Dot, который занимает в памяти 17 байт. Функция-член класса вызывается оператором: A. Dist (B), где объект А является текущим, а объект В – параметром.

Функция friend double Dist (const Dot & A, const Dot & B) возвращает значение расстояния между двумя заданными точками. Спецификатор const перед параметрами указывает компилятору, что состояние параметров не должно изменяться. В качестве параметров функция получает две ссылки на объекты типа Dot, которые занимает в памяти по 4 байта каждый. Функция вызывается оператором Dist (A, B). Поскольку функция является дружественной классу Dot, то доступ к закрытым членам x и y параметров A и B, которые являются объектами типа Dot, осуществляется с помощью оператора точка, например: A. x.

Функция double Dist (Dot * pA, Dot * pB) возвращает значение расстояния между двумя заданными точками. В качестве параметров функция получает два указателя на объекты типа Dot, которые занимает в памяти по 4 байта каждый. Функция вызывается оператором Dist (& A, & B). Поскольку функция не является ни членом класса Dot, ни дружественной классу к нему, то не может получить доступа к закрытым членам x и y параметров A и B. Получить значения членов x и y в этом случае можно только с помощью открытых функций-членов класса GetX () и GetY () соответственно, например: pA -> GetX (). Обратите внимание на то, что прототип глобальной функции мы расположили за пределами объявления класса.

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

Приведённый выше пример содержит также решение задачи вычисления площади треугольника с помощью глобальной функции double Area (const Dot & A, const Dot & B, const Dot & C), которая получает три ссылки на точки и возвращает значение площади треугольника. Функция вызывается оператором Area (A, B, С). Несмотря на то, что функция использует объекты типа Dot, тело функции не содержит обращений к закрытым членам класса. Поэтому мы не стали объявлять функцию как дружественную.

 

Функции, дружественные нескольким классам

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


Пример 3.

Файл DotVec. h

# include < iostream. h >

# include < windows. h >

# include < math.h >

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

class Dot // класс точки

{

const char name; // имя точки

double x, y; // координаты точки

public:

Dot( char Name): name(Name) { x =0; y =0;}

Dot( char Name, double X, double Y): name (Name) { x = X; y = Y;}

inline double GetX() const {return x;}

inline double GetY() const {return y;}

inline void SetX( double X) { x = X;}

inline void SetY( double Y) { y = Y;}

// вычисляет координаты конца заданного вектора

void EndVec (const Dot & A, const Vec & AB);

// объявление дружественной функции

friend void EndVec (const Dot & A, const Vec & AB, Dot & B);

};

class Vec // класс вектора

{

char name [3];

double x, y;

public:

Vec (char * pName) { strncpy (name, pName, 3); x =0; y =0;}

Vec (char * pName, double X, double Y) { strncpy (name, pName, 3); x = X; y = Y;}

// конструирование вектора по координатам его концов

Vec (char * pName, Dot A, Dot B);

double GetX () const {return x;}

double GetY () const {return y;}

// вычисляет координаты конца заданного вектора

void EndVec (const Dot & A, Dot & B);

// объявление дружественной функции

friend void EndVec (const Dot & A, const Vec & AB, Dot & B);

• • •

};

Файл DotVec. cpp

# include " DotVec. h "

Vec:: Vec (char * pName, Dot A, Dot B)

{

strncpy (name, pName, 3);

x = B.GetX () -A.GetX ();

y = B.GetY () -A.GetY ();

}

// три функции вычисляют координаты конца заданного вектора

void Dot:: EndVec (const Dot & A, const Vec & AB)

{

x = A. x + AB. GetX ();

y = A. y + AB. GetY ();

}

void Vec:: EndVec (const Dot & A, Dot & B)

{

B. SetX (A. GetX ()+ x);

B. SetY (A. GetY ()+ y);

}

void EndVec (const Dot & A, const Vec & AB, Dot & B)

{

B. x = A. x + AB. x;

B. y = A. y + AB. y;

}

Файл Main. cpp

# include " DotVec. h "

void main ()

{

Dot A ('A', 3, 4), B ('B',-3, 4);

Dot C ('C'), D ('D');

Vec AB ("AB", A, B);

Vec AC ("AC", 2, 2);

C. EndVec (A, AC);

AC. EndVec (A, C);

EndVec (A, AC, C);

}

Эта программа демонстрирует важный случай применения неполного объявления класса: без применения этой конструкции в данном случае было бы невозможно объявить дружественную функцию для двух классов. Неполное объявление класса Vec дает возможность использовать его имя в объявлении дружественной функции еще до то определения. Необходимо отметить, что при неполном объявлении класса объявления классов должны находиться в одном заголовочном файле, в данном случае DotVec. h.

В приведенном примере объявлены классы точки Dot и вектора Vec и поставленная задача решена тремя различными способами.

Функция void Dot:: EndVec (const Dot & A, const Vec & AB) является членом класса Dot, получает константные ссылки на вектор и начало вектора и передаёт координаты конца вектора в текущую точку. Поскольку закрытые члены-данные класса Vec недоступны в классе Dot, то мы используем открытые функции-члены класса VecGetY () и GetX (). Функция-член класса вызывается оператором: C. EndVec (A, AC), где объект C является текущим, а объекты A и AC – параметрами.

Функция void Vec:: EndVec (const Dot & A, Dot & B) является членом класса Vec, получает константную ссылку на начало вектора и ссылку на конец вектора. При вычислениях используются значения проекций текущего вектора. Поскольку закрытые члены-данные класса Dot недоступны в классе Vec, то мы используем открытые функции-члены класса DotGetX (), GetY (), SetX (), SetY (). Функция-член класса вызывается оператором: AC. EndVec (A, C), где объект AC является текущим, а объекты A и C – параметрами.

Функция friend void EndVec (const Dot & A, const Vec & AB, Dot & B) является дружественной классам Dot и Vec. Для этого она объявлена в обоих классах с ключевым словом friend. Функция получает константные ссылки на начало вектора и вектор, а также ссылку на конец вектора. Поскольку закрытые данные-члены обоих классов Dot и Vec доступны дружественной функции, то мы используем оператор «точка» для доступа к этим данным. Дружественная функция вызывается оператором EndVec (A, AC, C).

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

Функция может быть членом одного класса и дружественной другому классу. Для демонстрации этого синтаксического приёма немного изменим предыдущий пример.

Пример 4.

Файл DotVec. h

class Vec;

class Dot

{

• • •

public:

void EndVec (const Dot & A, const Vec & AB);

};

class Vec

{

• • •

public:

void EndVec (const Dot & A, Dot & B);

friend void Dot:: EndVec (const Dot & A, const Vec & AB);

};

Файл DotVec. cpp

# include " DotVec. h "

void Dot:: EndVec (const Dot & A, const Vec & AB)

{

x = A. x + AB. x;

y = A. y + AB. y;

}

void Vec:: EndVec (const Dot & A, Dot & B)

{

B. SetX (A. GetX ()+ x);

B. SetY (A. GetY ()+ y);

}

Функция void void Dot:: EndVec (const Dot & A, const Vec & AB) является членом класса Dot, но её прототип с ключевым словом friend включён также в объявление класса Vec. Таким образом, эта функция является дружественной классу Vec. Поскольку закрытые данные-члены класса Vec доступны дружественной функции, то мы используем оператор «точка» для доступа к данным-членам объекта типа Vec.

Следует отметить, что класс, дружественный функции, должен быть полностью объявлен ранее. Нам не удалось сделать функцию void Vec:: EndVec (const Dot & A, Dot & B) дружественной классу точки Dot, поскольку класс вектора Vec полностью объявлен позже полного объявления класса точки Dot.

Вызов приведённых функций не отличается от предыдущего примера.

Дружественные классы

C++ позволяет объявить не только дружественную функцию, но и дружественный класс, предоставив ему полный доступ к членам своего класса. Для этого достаточно включить в объявление класса имя другого класса, объявляемого дружественным, перед которым ставится ключевое слово friend.

 

Пример 5.

class A

{

friend class B;

• • •

};

Класс не может объявить сам себя другом некоторого другого класса. Для того, чтобы механизм дружественности сработал, он должен быть объявлен дружественным в этом другом классе.

Пример 6.

class Dot

{

friend class Vec; // класс Vec объявлен другом класса Dot

const char name;

double x, y;

public:

Dot (char Name): name (Name){ x =0; y =0;}

Dot (char Name, double X, double Y): name (Name) { x = X; y = Y;}

void Print () const;

};

class Vec

{

char name [ 3 ];

double x, y;

public:

Vec (char * pName) { strncpy (name, pName, 3); x =0; y =0;}

Vec (char * pName, double X, double Y) { strncpy (name, pName, 3); x = X; y = Y;}

Vec (char * pName, Dot A, Dot B);

void Print ();

void EndVec (const Dot & A, Dot & B);

};

Два класса могут объявить друг друга друзьями. С практической точки зрения такая ситуация свидетельствует о плохой продуманности иерархии классов, тем не менее, язык C++ допускает такую возможность. В этом случае объявления классов должны иметь следующий вид.

Пример 7.

class B; // необязательно!

class A

{

friend class B;

• • •

};

class B

{

friend class A;

• • •

};

Неполное объявление класса, которое приведено в данном фрагменте, может понадобиться, только если в классе A имеется ссылка на класс B, например, в параметре функции-члена.

По отношению к дружественным классам действуют следующие правила:

§ дружественность не является взаимным свойством: если A друг B, это не означает, что B – друг A;

§ дружественность не наследуется: если B – друг A, то классы, производные от B, не являются друзьями A;

§ дружественность не переходит на потомки базового класса: если B – друг A, то B не является другом для классов, производных от A.



<== предыдущая лекция | следующая лекция ==>
Закон состоит в следующем, что все элементы мира все частицы они соединяются друг с другом и при соединении растут | Функция Input ( ) объявлена невиртуальной в базовом классе Coord и переопределена в производных классах Dot и Vec
Поделиться с друзьями:


Дата добавления: 2016-11-03; Мы поможем в написании ваших работ!; просмотров: 546 | Нарушение авторских прав


Поиск на сайте:

Лучшие изречения:

Жизнь - это то, что с тобой происходит, пока ты строишь планы. © Джон Леннон
==> читать все изречения...

2294 - | 2065 -


© 2015-2024 lektsii.org - Контакты - Последнее добавление

Ген: 0.031 с.