Обратите внимание: если вы попытаетесь получить объект из класса Node и окажется, что не существует никакого объекта, то такая ситуация рассматривается как исключительная и исключение генерируется в строке 175.
В строках 182—183 определяется общий шаблон класса List. Этот класс может содержать узлы любых объектов, которые имеют уникальные идентификационные номера, кроме того, он сохраняет их отсортированными в порядке возрастания номеров. Каждая из функций списка проверяет ситуацию на исключительность и при необходимости генерирует соответствующие исключения.
В строках 307—308 управляющая программа создает список двух типов объектов класса Part, а затем печатает значения объектов в списке, используя стандартные потоки вывода.
Если бы в языке C++ поддерживалась контравариантность, можно было бы вызывать замещенные функции, основываясь на типе объекта указателя, на который ссылается указатель базового класса. Программа, представленная в листинге 3.2, демонстрирует суть контравариантности, но, к сожалению, ее нельзя будет скомпилировать в C++.
Вопросы и ответы
В комментарии, содержащемся в строках 65-69 говорится, что C++ не поддерживает контравариантность. Что такое контравариантность?
Контравариантностью называется возможность создания указателя базового класса на указатель производного класс.
Предупреждение:ВНИМАНИЕ: Этот листинг не будет скомпилирован!
Листинг 3.2. Пример контравариантности
#include <iostream.h>
class Animal
{
public:
virtual void Speak() { cout << "Animal Speaks\n";}
};
class Dog : public Animal
{
public:
void Speak() { cout << "Dog Speaks\n"; }
};
class Cat : public Animal
{
public:
void Speak() { cout << "Cat Speaks\n"; }
};
void DoIt(Cat*);
void DoIt(Dog*);
int main()
{
Animal * pA = new Dog;
DoIt(pA);
return 0;
}
void DoIt(Cat * с)
{
cout << "They passed а cat!\n" << endl;
c->Speak();
}
void DoIt(Dog * d)
{
cout << "They passed a dog!\n" << endl;
d->Speak();
}
Но в C++ эту проблему можно решить с помощью виртуальной функции.
#include<iostream.h>
class Animal
{
public:
virtual void Speak() { cout << "Animal Speaks\n"; }
};
class Dog : public Animal
{
public:
void Speak() { cout << "Dog Speaks\n"; }
};
class Cat : public Animal
{
public:
void Speak() { cout << "Cat Speaks\n"; }
};
void DoIt(Animal*);
int main()
{
Animal * pA = new Dog;
DoIt(pA);
return 0;
}
void DoIt(Animal * с)
{
cout << "They passed some kind of animal\n" << endl;
c->Speak();
}
Приложение А
Приоритеты операторов
Важно понять, что операторы имеют приоритеты, но запоминать их совсем не обязательно.
Приоритет оператора определяет последовательность, в которой программа выполняет операторы в выражении или формуле. Если один оператор имеет приоритет над другим оператором, то он выполняется первым.
Приоритет оператора убывает с увеличением номера категории. Все операторы одной категории имеют равный приоритет. Унарные операторы (категория 3), условный оператор (категория 14) и операторы присваивания (категория 15) ассоциируются справа налево, все остальные — слева направо. В приведенной ниже таблице операторы перечислены по категориям в порядке убывания их приоритетности.
Категория: 1 (Наивысшего приоритета)
Название или действие: Разрешение обасти видимости, индексирования
Оператор: :: []
Категория: 2
Название или действие: Прямое и косвенное обращение к члену класса
Оператор: . ->
Название или действие: Вызов функции
Оператор: ()
Название или действие: Постфиксные инкремент и декремент
Оператор: ++ --
Ктегория: 3 (унарные)
Название или действие: Префиксные инкремент и декремент
Оператор: ++ --
Название или действие: Размер
Оператор: sizeof, sizeof()
Название или действие: Дополнение до единицы и логическое отрицание
Оператор: ^ !
Название или действие: Унарные минус и плюс
Оператор: - +
Название или действие: Получение адреса и разыменование
Оператор: ? *
Название или действие: оздание и удаление динамического объекта
Оператор: new, new[], delete, delete[]