316:       cout << "(0)Quit (1)Car (2)Plane: ";
317:       cin >> choice;
318:
319:       if (!choice)
320:          break;
321:
322:       cout << " New PartNumber?: ";
323:       cin >> ObjectNumber;
324:
325:       if (choice == 1)
326:       {
327:          cout << "Model Year?: ";
328:          cin >> value;
329:          try
330:          {
331:             pPart = new CarPart(value,ObjectNumber);
332:          }
333:          catch (OutOfMemory)
334:          {
335:             cout << "Not enough memory; Exiting..." << endl;
336:             return 1;
337:          }
338:       }
339:       else
340:       {
341:          cout << "Engine Number?: ";
342:          cin >> value;
343:          try
344:          {
345:             pPart = new AirPlanePart(value,ObjectNumber);
346:          }
347:          catch (OutOfMemory)
348:          {
349:             cout << "Not enough memory: Exiting..." << endl;
350:             return 1;
351:          }
352:       }
353:       try
354:       {
355:          theList.Insert(pPart);
356:       }
357:       catch (NullNode)
358:       {
359:          cout << "The list is broken, and the node is null!" << endl;
360:          return 1;
361:       }
362:       catch (EmptyList)
363:       {
364:          cout << "The list is empty!" << endl;
365:          return 1;
366:       }
367:    }
368:    try
369:    {
370:       for (int i = 0; i < theList.GetCount(); i++ )
371:          cout << *(theList[i]);
372:    }
373:    catch (NullNode)
374:    {
375:       cout << "The list is broken, and the node is null!" << endl;
376:       return 1;
377:    }
378:    catch (EmptyList)
379:    {
380:       cout << "The list is empty!" << endl;
381:       return 1;
382:    }
383:    catch (BoundsError)
384:    {
385:       cout << "Tried to read beyond the end of the list!" << endl;
386:       return 1;
387:    }
388:    return 0;
389: }
Результат:
(0)Quit (1)Car (2)Plane: 1
New PartNumber?: 2837
Model Year? 90
(0)Quit (1)Car (2)Plane: 2
New PartNumber?: 378
Engine Number?: 4938
(0)Quit (1)Car (2)Plane: 1
New PartNumber?: 4499
Model Year? 94
(0)Quit (1)Car (2)Plane: 1
New PartNumber?: 3000
Model Year? 93
(0)Quit (1)Car (2)Plane: 0
Part Number: 378
Engine No. 4938
Part Number: 2837
Model Year: 90
Part Number: 3000
Model Year: 93
Part Number 4499
Model Year: 94
Анализ: Итоговая программа, основанная на материале за неделю 3, — это модификация программы, приведенной в обзорной главе по материалам за неделю 2. Изменения заключались в добавлении шаблона, обработке объекта ostream и исключительных ситуаций. Результаты работы обеих программ идентичны.
В строках 36—40 объявляется ряд классов исключений. В этой программе используется несколько примитивная обработка исключительных ситуаций. Классы исключений не содержат никаких данных или методов, они служат флагами для перехвата блоками catch, которые выводят простые предупреждения, а затем выполняют выход.
Более надежная программа могла бы передать эти исключения по ссылке, а затем извлечь контекст или другие данные из объектов исключения, чтобы попытаться исправить возникшую проблему.
В строке 45 объявляется абстрактный класс Part, причем точно так же, как это было сделано в листинге, обобщающем материал за неделю 2. Единственное интересное изменение здесь — это использование оператора operator<<(), который не является членом класса (он объявляется в строках 70—74). Обратите внимание, что он не является ни членом класса запчастей Part, ни другом класса Part. Он просто принимает в качестве одного из своих параметров ссылку на класс Part.
Возможно, вы бы хотели иметь замещенный оператор operator<<() для объектов классов CarPart и AirPlanePart с учетом различий в типах объектов. Но поскоДьку программа передает указатель на объект базового класса Part, а не указатель на указатель производных классов CarPart и AirPlanePart, то выбор правильной версии функции пришлось бы основывать не на типе объекта, а на типе одного из параметров функции. Это явление называется контравариантностью и не поддерживается в C++.
Есть только два пути достижения полиморфизма в C++: использование полиморфизма функций и виртуальных функций. Полиморфизм функций здесь не будет работать, сигнатуры функций, принимающих ссылку на класс Part, одинаковы.
Виртуальные функции также не будут здесь работать, поскольку оператор operator<< не является функцией-членом класса запчастей Part. Вы не можете сделать оператор operator<< функцией-членом класса Part, потому что в программе потребуется выполнить следующий вызов:
cout << thePart
Это означает, что фактически вызов относится к объекту cout.operator<<(Part&), а объект cout не имеет версии оператора operator<<, который принимает ссылку на класс запчастей Part!
Чтобы обойти это ограничение, в приведенной выше программе используется только один оператор operator<<, принимающий ссылку на класс Part. Затем вызывается метод Display(), который является виртуальной функцией-членом, в результате чего вызывается правильная версия этого метода.
В строках 130—143 класс Node определяется как шаблон. Он играет ту же роль, что и класс Node в программе из обзора за неделю 2, но эта версия класса Node не связана с объектом класса Part. Это значит, что данный класс может создавать узел фактически для любого типа объекта.
Обратите внимание: если вы попытаетесь получить объект из класса 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 Speaksn";}
};
class Dog : public Animal
{
   public:
      void Speak() { cout << "Dog Speaksn"; }
};
class Cat : public Animal
{
   public:
      void Speak() { cout << "Cat Speaksn"; }
};
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 Speaksn"; }
};
class Dog : public Animal
{
   public:
      void Speak() { cout << "Dog Speaksn"; }
};
class Cat : public Animal
{
   public:
      void Speak() { cout << "Cat Speaksn"; }
};
void DoIt(Animal*);
int main()
{
   Animal * pA = new Dog;
   DoIt(pA);
   return 0;
}
void DoIt(Animal * с)
{
   cout << "They passed some kind of animaln" << endl;
   c->Speak();
}
Приложение А
Приоритеты операторов 
 Важно понять, что операторы имеют приоритеты, но запоминать их совсем не обязательно.
Приоритет оператора определяет последовательность, в которой программа выполняет операторы в выражении или формуле. Если один оператор имеет приоритет над другим оператором, то он выполняется первым.
Приоритет оператора убывает с увеличением номера категории. Все операторы одной категории имеют равный приоритет. Унарные операторы (категория 3), условный оператор (категория 14) и операторы присваивания (категория 15) ассоциируются справа налево, все остальные — слева направо. В приведенной ниже таблице операторы перечислены по категориям в порядке убывания их приоритетности.
Категория: 1 (Наивысшего приоритета)
   Название или действие: Разрешение обасти видимости, индексирования