Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Либерти Джесс. Освой самостоятельно С++ за 21 день. - royallib.ru.rtf
Скачиваний:
1
Добавлен:
01.07.2025
Размер:
2.55 Mб
Скачать

Коллоквиум

В этом разделе предлагаются вопросы для самоконтроля и укрепления полученных знаний и приводится несколько упражнений, которые помогут закрепить ваши практические навыки. Попытайтесь самостоятельно ответить на вопросы теста и выполнить задания, а потом сверьте полученные результаты с ответами в приложении Г. Не приступайте к изучению материала следующей главы, если для вас остались неясными хотя бы некоторые из предложенных ниже вопросов.

Контрольные вопросы

1. Что такое приведение типа объекта вниз?

2. Что такое v-ptr?

3. Предположим, для создания прямоугольника с закругленными углами используется класс RoundRect, произведенный от двух базовых классов — Rectangle и Circle, которые, в свою очередь, производятся от общего класса Shape. Как много объектов класса Shape создается при создании одного объекта класса RoundRect?

4. Если классы Horse и Bird виртуально наследуются от класса Animal как открытые, будут ли конструкторы этих классов инициализировать конструктор класса Animal? Если класс Pegasus наследуется сразу от двух классов, Horse и Bird, как в нем будет инициализироваться конструктор класса Animal?

5. Объявите класс Vehicle (Машина) как абстрактный тип данных.

6. Если в программе объявлен класс ADT с тремя чистыми виртуальными функциями, сколько из них нужно заместить в производных классах, чтобы получить возможность создания объектов этих классов?

Упражнения

1. Объявите класс JetPlane (Реактивный самолет), наследуя его отдвух базовых классов — Rocket (Ракета) и Airplane (Самолет).

2. Произведите от класса JetPlane, объявленного в первом упражнении, новый класс 747.

3. Напишите программу, производящую классы Car (Легковой автомобиль) и Bus (Автобус) от класса Vehicle (Машина). Объявите класс Vehicle как абстрактный тип данных с двумя чистыми виртуальными функциями. Классы Car и Bus не должны быть абстрактными.

4. Измените программу из предыдущего упражнения таким образом, чтобы класс Car тоже стал ADT, и произведите от него три новых класса: SportsCar (Спортивный автомобиль), Wagon (Фургон) и Coupe (Двухместный автомобиль-купе). В классе Car должна замещаться одна из виртуальных функций, объявленных в классе Vehicle, с вызовом функции базового класса.

День 14-й. Специальные классы и функции

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

• Что такое статические переменные-члены и функции-члены

• Как используются статические переменные-члены и функции-члены

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

• Как работать с массивами указателей на функции

Статические переменные-члены

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

Однако иногда возникает необходимость контроля за накоплением данных программой. Может потребоваться информация о том, сколько всего было создано объектов определенного класса и сколько их существует в данный момент. Статические переменные-члены совместно используются всеми объектами класса. Они являются чем вроде "золотой серединки" между глобальными данными, доступными всем частям программы, и данными членов, доступными, как правило, только одному объекту.

Можно полагать, что статические члены принадлежат классу, а не объекту. Если данные обычных членов доступны одному объекту, то статические члены могут использоваться всем классом. В листинге 14.1 объявляется объект Cat со статическим членом HowManyCats. Эта переменная учитывает количество созданных объектов Cat, что реализуется приращением статической переменной HowManyCats при вызове конструктора или отрицательным приращением при вызове деструктора.

Листинг 14.1. Статические переменные-члены

1: //Листинг 14.1. Статические переменные-члены

2:

3: #include <iostream.h>

4:

5: class Cat

6: {

7:    public:

8:       Cat(int age):itsAge(age){ HowManyCats++; }

9:       virtual ~Cat() { HowManyCats--; }

10:      virtual int 6etAge() { return itsAge; }

11:      virtual void SetAge(int age) { itsAge = age; }

12:      static int HowManyCats;

13:

14:   private:

15:      int itsAge;

16:

17: };

18:

19: int Cat::HowManyCats = 0;

20:

21: int main()

22: {

23:    const int MaxCats = 5;

24:    int i; Cat *CatHouse[MaxCats];

25:    for (i = 0; i<MaxCats; i++)

26:       CatHouse[i] = new Cat(i);

27:

28:    for (i = 0; i<MaxCats; i++)

29:    {

30:       cout << "There are ";

31:       cout << Cat::HowManyCats;

32:       cout << " cats left!\n";

33:       cout << "Deleting the one which is ";

34:       cout << CatHouse[i]->GetAge();

35:       cout << " yea.rs old\n";

36:       delete CatHouse[i];

37:       CatHouse[i] = 0;

38:    }

39:    return 0;

40: }

Результат:

There are 5 cats left!

Deleting the one which is 0 years old

There are 4 cats left!

Deleting the one which is 1 years old

There are 3 cats left!

Deleting the one which is 2 years old

There are 2 cats left!

Deleting the one which is 3 years old

There are 1 cats left!

Deleting the one which is 4 years old

Анализ: Обычный класс Cat объявляется в строках 5—17. С помощью ключевого слова static в строке 12 объявляется статическая переменная-член

HowManyCats типа int.

Объявление статической переменной HowManyCats само по себе не определяет никакого целочисленного значения, т.е. в памяти компьютера не резервируется область для данной переменной при ее объявлении, поскольку, по сути, она не является переменной-членом конкретного объекта Cat. Определение и инициализация переменной HowManyCats происходит в строке 19.

Не забывайте отдельно определять статическую переменную-член класса (весьма распространенная ошибка среди начинающих программистов). В противном случае редактор связей во время компиляции программы выдаст следующее сообщение об ошибке:

undefined symbol Cat::HowManyCats

Обратите внимание, что для обычной переменной-члена itsAge не требуется отдельное определение, поскольку обычные переменные-члены определяются автоматически каждый раз при создании объекта Cat, как, например, в строке 26.

Конструктор объекта Cat, объявленный в строке 8, увеличивает значение статической переменной-члена на единицу. Деструктор, объявленный в строке 9, уменьшает это значение на 1. Таким образом, в любой момент времени переменная HowManyCats отражает текущее количество созданных объектов класса Cat.

В строках программы 21—40 создается пять объектов Cat, указатели на которые заносятся в массив. Это сопровождается пятью вызовами конструктора класса Cat, в результате чего пять раз происходит приращение на единицу переменной HowManyCats, начиная с исходного значения 0.

Затем в программе цикл for последовательно удаляет все объекты Cat из массива, предварительно выводя на экран текущее значение переменной HowManyCats. Вывод начинается со значения 5 (ведь было создано пять объектов) и с каждым циклом уменьшается.

Обратите внимание: переменная HowManyCats объявлена как public и может вызываться из функции main(). Однако нет веских причин объявлять эту переменную-член таким образом. Если предполагается обращаться к статической переменной только через объекты класса Cat, предпочтительней сделать ее закрытой вместе с другими переменными-членами и создать открытый метод доступа. С другой стороны, если необходимо получать прямой доступ к данным без использования объекта Cat, то можно либо оставить ее открытой, как показано в листинге 14.2, либо создать статическую функцию-член. Реализация последнего варианта рассматривается далее в этой главе.

Листинг 14.2. Доступ к статическим членам без использования объектов  

1: // Листинг 14.2. Статические переменные-члены

2:

3: #include <iostream.h>

4:

5: class Cat

6: {

7:    public:

8:       Cat(int age):itsAge(age) { HowManyCats++; }

9:       virtual ~Cat() { HowManyCats--; }

10:      virtual int GetAge() { return itsAge; }

11:      virtual void SetAge(int age) {itsAge = age;}

12:      static int HowManyCats;

13:

14:   private:

15:      int itsAge;

16:

17: };

18:

19: int Cat::HowManyCats = 0;

20:

21: voidTelepathicFunction();

22:

23: int main()

24: {

25:    const int MaxCats = 5; int i;

26:    Cat *CatHouse[MaxCats];

27:    for (i = 0; i<MaxCats; i++)

28:    {

29:       CatHouse[i] = new Cat(i);

30:       TelepathicFunction();

31:    }

32:

33:    for ( i = 0; i<MaxCats; i++)

34:    {

35:       delete CatHouse[i];

36:       TelepathicFunction();

37:    }

38:    return 0;

39: }

40:

41: void TelepathicFunction()

42: {

43:    cout << "There are ";

44:    cout << Cat::HowManyCats << " cats alive!\n";

45: }

Результат:

There are 1 cats alive!

There are 2 cats alive!

There are 3 cats alive!

There are 4 cats alive!

There are 5 cats alive!

There are 4 cats alive!

There are 3 cats alive!

There are 2 cats alive!

There are 1 cats alive!

There are 0 cats alive!

Анализ: Листинг 14.2 аналогичен листингу 14.1, однако включает новую функцию TelepahicFunction().Она не создает объект СаГ и даже не использует тегов качестве параметра, однако может получить доступ к переменной-члену HowManyCats. Не лишним будет еще раз напомнить, что эта переменная-член относится не к какому-либо определенному объекту, а ко всему классу в целом. Поэтому если она объявлена как public, то может использоваться любой функцией программы.

Если статическая переменная-член будет объявлена как закрытая, то доступ к ней можно получить с помощью функции-члена. Но для этого необходимо наличие хотя бы одного объекта данного класса. Именно такой подход реализован в листинге 14.3. Затем мы перейдем к изучению статических функций-членов.

Листинг 14.3. Доступ к статическим членам с помощью обычных функций-членов

1: //Листинг 14.3. Закрытые статические переменные-члены

2:

3: #include <iostream.h>

4:

5: class Cat

6: {

7:    public:

8:       Cat(int age):itsAge(age){ HowManyCats++; }

9:       virtual ~Cat() { HowManyCats--; }

10:      virtual int GetAge() { return itsAge; }

11:      virtual void SetAge(int age) { itsAge = age; }

12:      virtual int GetHowMany() { return HowManyCats; }

13:

14:

15:   private:

16:      int itsAge;

17:      static int HowManyCats;

18: };

19:

20: int Cat::HowManyCats = 0;

21:

22: int main()

23: {

24:    const int MaxCats = 5; int i;

25:    Cat *CatHouse[MaxCats];

26:    for (i = 0; i<MaxCats; i++)

27:       CatHouse[i] = new Cat(i);

28:

29:    for (i = 0; i<MaxCats; i++)

30:    {

31:       cout << "There are ";

32:       cout << CatHouse[i]->GetHowMany();

33:       cout << " cats left!\n";

34:       cout << "Deleting the one which is ";

35:       cout << CatHouse[i]->GetAge()+2;

36:       cout << " years old\n";

37:       delete CatHouse[i];

38:       CatHouse[i] = 0;

39:    }

40:    return 0;

41: }

Результат:

There are 5 cats left!

Deleting the one which is 2 years old

There are 4 cats left!

Deleting the one which is 3 years old

There are 3 cats left!

Deleting the one which is 4 years old

There are 2 cats left!

Deleting the one which is 5 years old

There are 1 cats left!

Deleting the one which is 6 years old

Анализ: В строке 17 статическая переменная-член HowManyCats объявлена как private. Поэтому теперь доступ к ней закрыт для функций, не являющихся членами класса, например для функции TelepathicFunction из предыдущего листинга.

Хотя переменная HowManyCats является статической, она все же находится в области видимости класса. Поэтому любая функция класса, например GetHoqMany(), может получить доступ к ней так же, как к любой обычной переменной-члену. Однако для вызова GetHowMany() функция должна иметь объект, через который осуществляется вызов.

Рекомендуется: Применяйте статические переменные-члены для совместного использования данных несколькими объектами класса. Ограничьте доступ к статическим переменным-членам, объявивих как private или protected.

Не рекомендуется: Не используйте статические перемен- ные-члены для хранения данных одного объекта. Эти переменные предназначены для обмена данными между объектами.