Студопедия

Главная страница Случайная страница

КАТЕГОРИИ:

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






Листинг 3.1. Программа, основанная на материалах недели 3






1: // ************************************

2: //

3: // Название: Обзор недели 3

4: //

5: // Файл: Week3

6: //

7: // Описание: Программа с использованием связанного списка

8: // на основе шаблона с обработкой исключительных ситуаций

9: //

10: // Классы: PART - хранит номера запчастей и потенциально другую

11: // информацию о запчастях. Зто будет

12: // пример класса для хранения списка.

13: // Обратите внимание на использование

14: // оператора < < для печати информации о запчасти

15: // на основе его типа и времени выполнения,

16: //

17: // Node - действует как узел в классе List

18: //

19: // List - список, основанный на шаблоне, который

20: // обеспечивает работу связанного списка

21: //

22: //

23: // Автор: Jesse Liberty (jl)

24: //

25: // Разработан: Pentium 200 Pro. 128MB RAM MVC 5.0

26: //

27: // Требования: Не зависит от платформы

28: //

29: // История создания: 9/94 - Первый выпуск (jl)

30: // 4/97 - Обновлено (jl)

31: // ************************************

32:

33: #include < iostream.h>

34:

35: // классы исключений

36: class Exception { };

37: class OutOfMemory: public Exception{ };

38: class NullNode: public Exception{ };

39: class EmptyList: public Exception { };

40: class BoundsError: public Exception { };

41:

42:

43: // **************** Part **************

44: // Абстрактный базовый класс запчастей

45: class Part

46: {

47: public:

48: Part(): its0bjectNumber(1) { }

49: Part(int 0bjectNumber): its0bjectNumber(ObjectNumber){ }

50: virtual ~Part(){ };

51: int GetObjectNumber() const { return itsObjectNumber; }

52: virtual void Display() const =0; // функция будет замещена в производном классе

53:

54: private:

55: int itsObjectNumber;

56: };

57:

58: // выполнение чистой виртуальной функции, необходимой

59: // для связывания объектов производного класса

60: void Part:: Display() const

61: {

62: cout < < " \nPart Number: " < < itsObjectNumber < < endl;

63: }

64:

65: // Этот оператор < < будет вызываться для всех объектов запчастей.

66: // Его не нужно объявлять другом, поскольку он не обращается к закрытым данным.

67: // Он вызывает метод Display(), в результате чего реализуется полиморфизм классов.

68: // Было бы не плохо замещать функцию оператора для разных

69: // типов thePart, но C++ не поддерживает контравариантность

70: ostream& operator< < (ostream& theStream, Part& thePart)

71: {

72: thePart.Display(); // косвенная реализация полиморфизма оператора вывода!

73: return theStream;

74: }

75:

76: // **************** Car Part ************

77: class CarPart: public Part

78: {

79: public:

80: CarPart(): itsModelYear(94){ }

81: CarPart(int year, int partNumber);

82: int GetModelYear() const { return itsModelYear; }

83: virtual void Display() const;

84: private:

85: int itsModelYear;

86: };

87:

88: CarPart:: CarPart(int year, int partNumber):

89: itsModelYear(year),

90: Part(partNumber)

91: { }

92:

93: void CarPart:: Display() const

94: {

95: Part:: Display();

96: cout < < " Model Year: " < < itsModelYear < < endl;

97: }

98:

99: // **************** AirPlane Part ************

100: class AirPlanePart: public Part

101: {

102: public:

103: AirPlanePart(): itsEngineNumber(1){ };

104: AirPlanePart(int EngineNumber, int PartNumber);

105: virtual void Display() const;

106: int GetEngineNumber()const { return itsEngineNumber; }

107: private:

108: int itsEngineNumber;

109: };

110:

111: AirPlanePart:: AirPlanePart(int EngineNumber, int PartNumber):

112: itsEngineNumber(EngineNumber),

113: Part(PartNumber)

114: { }

115:

116: void AirPlanePart:: Display() const

117: {

118: Part:: Display();

119: cout < < " Engine No,: " < < itsEngineNumber < < endl;

120: }

121:

122: // Обьявление класса List

123: template < class T>

124: class List;

125:

126: // **************** Node ************

127: // Общий узел, который можно добавить к списку

128: // **********************************

129:

130: template < class T>

131: class Node

132: {

133: public:

134: friend class List< T>;

135: Node (T*);

136: ~Node();

137: void SetNext(Node * node) { itsNext = node; }

138: Node * GetNext() const;

139: T * GetObject() const;

140: private:

141: T* its0bject;

142: Node * itsNext;

143: };

144:

145: // Выполнение узла...

146:

147: template < class T>

148: Node< T>:: Node(T* p0jbect):

149: itsObject(pOjbect),

150: itsNext(0)

151: { }

152:

153: template < class T>

154: Node< T>:: ~Node()

155: {

156: delete its0bject;

157: itsObject = 0;

158: delete itsNext;

159: itsNext = 0;

160: }

161:

162: // Возвращает значение NULL, если нет следующего узла

163: template < class T>

164: Node< T> * Node< T>:: GetNext() const

165: {

166: return itsNext;

167: }

168:

169: template < class T>

170: T * Node< T>:: GetObject() const

171: {

172: if (itsObject)

173: return itsObject;

174: else

175: throw NullNode();

176: }

177:

178: // **************** List ************

179: // Общий шаблон списка

180: // Работает с любым нумерованным объектом

181: // **********************************

182: template < olass T>

183: class List

184: {

185: public:

186: List();

187: ~List();

188:

189: T* Find(int & position, int 0bjectNumber) const;

190: T* GetFirst() const;

191: void Insert(T *);

192: T* operator[](int) const;

193: int GetCount() const { return itsCount; }

194: private:

195: Node< T> * pHead;

196: int itsCount;

197: };

198:

199: // Выполнение списка...

200: template < class T>

201: List< T>:: List();

202: pHead(0),

203: itsCount(0)

204: { }

205:

206: template < class T>

207: List< T>:: ~List()

208: {

209: delete pHead;

210: }

211:

212: template < class T>

213: T* List< T>:: GetFirst() const

214: {

215: if (pHead)

216: return pHead-> itsObject;

217: else

218: throw EmptyList();

219: }

220:

221: template < class T>

222: T * List< T>:: operator[](int offSet) const

223: {

224: Node< T> * pNode = pHead;

225:

226: if (! pHead)

227: throw EmptyList();

228:

229: if (offSet > itsCount)

230: throw BoundsError();

231:

232: for (int i=0; i< offSet; i++)

233: pNode = pNode-> itsNext;

234:

235: return pNode-> itsObject;

236: }

237:

238: // Находим данный обьект в списке на основе его идентификационного номера (id)

239: template < class T>

240: T* List< T>:: Find(int & position, int 0bjectNumber) const

241: {

242: Node< T> * pNode = 0;

243: for (pNode = pHead, position = 0;

244: pNode! =NULL;

245: pNode = pNode-> itsNext, position++)

246: {

247: if (pNode-> itsObject-> GetObjectNumber() == 0bjectNumber)

248: break;

249: }

250: if (pNode == NULL)

251: return NULL;

252: else

253: return pNode-> itsObject;

254: }

255:

256: // добавляем в список, если номер объекта уникален

257: template < class T>

258: void List< T>:: Insert(T* pObject)

259: {

260: Node< T> * pNode = new Node< T> (p0bject);

261: Node< T> * pCurrent = pHead;

262: Node< T> * pNext = 0;

263:

264: int New = p0bject-> Get0bjectNumber();

265: int Next = 0;

266: itsCount++;

267:

268: if (! pHead)

269: {

270: pHead = pNode;

271: return;

272: }

273:

274: // если номер текущего объекта меньше номера головного,

275: // то этот объект становится новым головным узлом

276: if (pHead-> itsObject-> GetObjectNumber() > New)

277: {

278: pNode-> itsNext = pHead;

279: pHead = pNode;

280: return;

281: }

282:

283: for (;;)

284: {

285: // если нет следующего обьекта, добавляем в конец текущий объект

286: if (! pCurrent-> itsNext)

287: {

288: pCurrent-> itsNext = pNode;

289: return;

290: }

291:

292: // если данный объект больше текущего, но меньше следующего,

293: // то вставляем его между ними, в противном случае переходим к следующему объекту

294: pNext = pCurrent-> itsNext;

295: Next = pNext-> itsObject-> GetObjectNumber();

296: if (Next > New)

297: {

298: pCurrent-> itsNext = pNode;

299: pNode-> itsNext = pNext;

300: return;

301: }

302: pCurrent = pNext;

303: }

304: }

305:

306:

307: int main()

308: {

309: List< Part> theList;

310: int choice;

311: int ObjectNumber;

312: int value;

313: Part * pPart;

314: while (1)

315: {

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 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();

}

 

 


Поделиться с друзьями:

mylektsii.su - Мои Лекции - 2015-2024 год. (0.047 сек.)Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав Пожаловаться на материал