ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 13.12.2020
Просмотров: 4295
Скачиваний: 28
Часть II. Язык программирования С++
186
Определение класса
PrintBox
поместим в заголовочный файл
PrintBox.h, текст которого приведен в листинге 14.6.
Листинг 14.6. Определение дружественного класса
#include "IntBox.h"
#include <iostream>
using namespace std;
#if !defined PRINT_BOX
#define PRINT_BOX
struct PrintBox
{
void print(IntBox& box);
};
#endif
Реализацию функции
из класса
PrintBox
поместим в исход-
ный файл PrintBox.cpp, текст которого приведен в листинге 14.7.
Листинг 14.7. Реализация функции дружественного класса
#include "PrintBox.h"
void PrintBox::print(IntBox& box) { cout << box.room; }
В листинге 14.8 показано, как распечатывать содержимое кон-
тейнера при помощи функции
.
Листинг 14.8. Использование функции дружественного класса
#include "IntBox.h"
#include "PrintBox.h"
#include <iostream>
using namespace std;
int main()
{
IntBox box;
PrintBox prn;
Глава 14. Классы
187
box.put(10);
prn.print(box); // печатает 10
cout << endl;
return 0;
}
При использовании друзей класса следует учитывать следующие
правила:
отношение дружбы не является симметричным, т. е. если А
есть друг В, то из этого не следует, что В есть друг А;
отношение дружбы не является транзитивным, т. е. если А
друг В и В друг С, то из этого не следует, что А является дру-
гом С;
отношение дружбы не наследуется, т. е. если класс А является
другом класса С и предком класса В, то из этого не следует,
что класс В является другом класса С.
14.7. Встроенные функции-члены
класса
Если функция-член класса определена прямо в теле класса, то она
считается
встроенной
. В этом случае ключевое слово
inline
можно не писать. Например, определим все функции класса
IntBox
как встроенные.
class IntBox
{
bool empty;
int room;
public:
IntBox() { empty = true; }
void put(int n) { room = n; empty = false; }
int get() { empty = true; return room; }
bool isEmpty() { return empty; }
};
В этом случае исходный файл IntBox.cpp нужно исключить из
проекта.
Часть II. Язык программирования С++
188
Если при объявлении функции-члена класса используется ключе-
вое слово
inline
, то определять функцию можно и вне тела клас-
са, при этом слово
inline
можно опустить. Так как встроенные
функции-члены класса должны быть определены в каждом ис-
ходном файле, где они вызываются, то определение этих функ-
ций следует поместить в тот же заголовочный файл, где описан
класс.
14.8. Функции-члены класса
с квалификаторами
const
и
volatile
Функции-члены класса могут иметь квалификатор
const
, или
volatile
, или два этих квалификатора вместе. Эти квалификато-
ры записываются после имени функции и имеют следующее на-
значение.
Для константного объекта класса могут быть вызваны только те
функции-члены класса, которые объявлены с квалификатором
const
. Для неконстантного объекта класса могут быть вызваны
функции как с квалификатором
const
, так и без него. Поэтому
функции-члены класса, которые не изменяют значения его атри-
бутов, нужно объявлять с квалификатором
const
.
Запрещено объявлять константную функцию-член класса, кото-
рая модифицирует члены класса прямо или косвенно через не-
константные функции-члены класса. Константную функцию-член
класса можно перегружать неконстантной функцией с тем же
списком параметров.
Учитывая это замечание, переопределим класс
IntBox
следую-
щим образом:
class IntBox
{
bool empty;
int room;
public:
IntBox(); // инициализация пустого контейнера
IntBox(int n); // инициализация полного контейнера
Глава 14. Классы
189
void put(int n);
int get();
int get() const;
bool isEmpty() const;
};
Обратим внимание на следующих два момента. Во-первых, по-
явился новый конструктор, который позволяет определять кон-
тейнер и сразу заносить в него число. Также появился новый ме-
тод
get
с квалификатором
const
, который позволит просматри-
вать содержимое константного контейнера. Реализация этих но-
вых функций выглядит следующим образом:
IntBox::IntBox(int n) { empty = false; room = n; }
int IntBox::get() const { return room; }
А реализация функции
isEmpty()
не изменится. Теперь можно
определять константные контейнеры для хранения целых чисел и
определять их содержимое. Ниже приведен пример использова-
ния константного контейнера.
int main()
{
const IntBox box(10);
int n;
if (!box.isEmpty())
n = box.get();
cout << n << endl; // печатает 10
return 0;
}
Для объекта класса, объявленного со спецификатором доступа
volatile
, могут быть вызваны только те функции-члены класса,
которые также объявлены со спецификатором доступа
volatile
.
Для объекта класса, объявленного без квалификатора
volatile
,
могут быть вызваны функции как с квалификатором
volatile
, так
и без него. Функцию-член класса с квалификатором
volatile
можно перегружать функцией-членом класса с тем же списком
параметров, но без этого квалификатора.
Часть II. Язык программирования С++
190
Если в классе перегружены функции-члены с квалификаторами
const
и
volatile
, то при вызове такой функции для объекта клас-
са, объявленного без квалификаторов
const
и
volatile
, компиля-
тор выдаст ошибку неоднозначного выбора функции.
14.9. Данные-члены класса
с квалификатором
mutable
Чтобы разрешить модификацию члена класса, принадлежащего
константному объекту, этот член класса должен быть объявлен
с квалификатором
mutable
. Пример использования этого квали-
фикатора приведен в листинге 14.9.
Листинг 14.9. Использование квалификатора
mutable
#include <iostream>
using namespace std;
class counter
{
mutable int n; // счетчик обращений
public:
counter() { n = 0; };
int count() const { return ++n; }
};
int main()
{
const counter n;
cout << n.count() << endl; // печатает 1
return 0;
}
14.10. Статические члены класса
Статические члены класса имеют класс памяти
static
и являются
глобальными объектами, которые принадлежат всему классу и
существуют в единственном экземпляре.