KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программирование » Джесс Либерти - Освой самостоятельно С++ за 21 день.

Джесс Либерти - Освой самостоятельно С++ за 21 день.

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Джесс Либерти, "Освой самостоятельно С++ за 21 день." бесплатно, без регистрации.
Перейти на страницу:

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 (Наивысшего приоритета)

   Название или действие: Разрешение обасти видимости, индексирования

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*