Где получить справочную информацию и советы

Первое, что вам стоит сделать, — отыскать в Internet одну из конференций по C++. Эти группы поддерживают непосредственный контакт с сотнями и даже тысячами программистов C++, которые смогут ответить на ваши вопросы, предложить советы и подсказать решения для ваших идей.

Я принимаю участие в группах новостей Internet, посвященных C++ (comp.lang.c++ и comp.lang.c++.moderated), и рекомендую их в качестве превосходных источников информации и поддержки.

Кроме того, стоит поискать локальные группы пользователей. Во многих городах есть так называемые группы по интересам (в том числе и группы C++), в которых можно встретить других программистов и обменяться идеями.

Журналы

Закрепить свои навыки можно, подписавшись на хороший журнал, посвященный программированию на языке C++. По моему мнению, самым лучшим журналом по этой тематике является C++ Report издательства SIGS Publications. Каждый выпуск этого журнала содержит полезные статьи, поэтому их стоит сохранять — ведь то, что не волнует вас сегодня, станет чрезвычайно важным уже завтра. (Предостережение: я написал об этом журнале в первом и втором издании книги, но теперь я веду в нем ежемесячную рубрику, и потому налицо конфликт интересов. Тем не менее я по- прежнему считаю, что этот журнал — потрясающее издание.)

Журнал C++ Report можно приобрести в издательстве SIGS Publications по адресу: P.O. Box 2031, Langhorne, РА 19047-9700.

Выскажите свое мнение о книге

Если у вас есть комментарии, предложения или замечания относительно этой или других книг, я бы с интересом их выслушал. Пишите мне по адресу: [email protected] или посетите мой Web-узел: www.libertyassociates.com. Я с нетерпением буду ждать ваши отзывы.

Рекомендуется: Обратитесь к другим книгам по C++. Нет такой книги, в которой все темыбыли бы рассмотрены одинаково полно и которая могла бы научить всему, что нужно знать профессиональному программисту C++. Подпишитесь на хороший журнал по C++ и подключитесь к хорошей группе шовостейпользователейC++.

Не рекомендуется: Не ограничивайтесь при освоении C++ только чтением чужих программ. Лучший способ изучения языка — самому писать программы.

Резюме

Сегодня вы узнали много подробностей о работе с препроцессором. При каждом запуске компилятора сначала запускается препроцессор, который расшифровывает и выполняет такие директивы, как #define и #ifdef.

Препроцессор осуществляет текстовую подстановку, хотя использование макросов может несколько усложнить чтение программы. С помощью таких директив, как #ifdef, #else и #ifndef, можно выполнять условную компиляцию, которая позволяет компилировать один набор команд при одних условиях, и другой — при других условиях. Благодаря этому можно писать программы сразу для нескольких платформ и успешно выполнять их отладку.

Макросы обеспечивают сложную текстовую подстановку на основе параметров, передаваемых им во время компиляции программы. Чтобы гарантировать правильное выполнение подстановки, каждый параметр макроса нужно заключить в круглые скобки.

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

Кроме того, вы узнали, как устанавливать и возвращать значения отдельных битов и как выделять ограниченное число битов для переменных-членов класса.

Наконец, вы получили информацию о стиле написания программы на языке C++, а также о том, с помощью каких первоисточников лучше продолжить изучение C++.

Вопросы и ответы

Если C++ предлагает лучшие решения, чем препроцессор, почему же эти средства все еще доступны?

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

Зачем использовать макросы там, где можно использовать обычные функции?

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

В каких случаях лучше использовать макрос, чем подставляемую функцию?

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

Какая альтернатива использованию препроцессора для печати промежуточных значений в процессе отладки?

Лучше всего использовать оператор watch внутри отладчика. За информацией о его использовании обращайтесь к документации, прилагаемой к вашему компилятору или отладчику.

Когда лучше использовать макрос assert(), а когда — исключение?

Если ошибка в выполнении программы может возникнуть из-за внешних причин, таких как некорректный ввод данных пользователем или несовершенство компьютерной системы, используйте исключения. Если же сбой программы возникает вследствие синтаксических или логических ошибок в коде программы, используйте макрос assert().

Когда лучше использовать битовые поля вместо обычных переменных?

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

Почему споры о стилях столь эмоциональны?

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

if (НекотороеУсловие){

// выражения

} // закрывающая фигурная скобка

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

C++ или какую программу лучше всего использовать в качестве текстового процессора. Затем усаживайтесь поудобнее и приготовьтесь прочитать тысяч десять противоречащих друг другу сообщений.

Пришло время прощаться?

Да! Вы изучили C++, хотя... нет. Еще десять лет назад один человек мог изучить все, что было известно в мире о микро ЭВМ, или, по крайней мере, чувствовать себя вполне уверенно в этом вопросе. Сегодня это исключено даже теоретически. Одному человеку невозможно разобраться во всем, и даже за то время, пока вы попытаетесь это сделать, ситуация в индустрии программирования изменится, а значит, вы опять отстанете от нее. Тем не менее обязательно продолжайте читать, постоянно обращайтесь к различным источникам — журналам и группам новостей, которые будут держать вас в курсе самых последних новшеств в этой области.

Коллоквиум

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

1. Для чего нужны средства защиты от повторного включения?

2. Как указать компилятору, что необходимо напечатать содержимое промежуточного файла, полученного в результате работы препроцессора?

3. Какова разница между директивами #define debug 0 и #undef debug?

4. Что делает оператор дополнения до единицы?

5. Чем отличается оператор побитового ИЛИ от оператора исключающего побитового ИЛИ?

6. Какова разница между операторами & и &&?

7. В чем разница между операторами | и || ?

Упражнения

1. Создайте защиту от повторного включения файла заголовка STRING, н.

2. Напишите макрос assert(), который

• будет печатать сообщение об ошибке, а также имя файла и номер строки, если уровень отладки равен 2;

• будет печатать сообщение (без имени файла и номера строки), если уровень отладки равен 1;

• не будет ничего делать, если уровень отладки равен 0.

3. Напишите макрос DPrint, который проверяет, определена ли лексема DEBUG, и, если да, выводит значение, передаваемое как параметр.

4. Напишите программу, которая складывает два числа без использования операции сложения (+). Подсказка: используйте побитовые операторы!

Подведение итогов

В приведенной ниже программе используются многие "продвинутые" методы, с которыми вы познакомились на протяжении трех недель усердных занятий. Программа содержит связанный список, основанный на шаблоне; кроме того, в ней проводится обработка исключительных ситуаций. Тщательно разберитесь в этой программе, и, если полностью ее поймете, значит, вы — программист C++.

Предупреждение: Если ваш компилятор не поддерживает шаблоны или блоки try и catch, вы не сможете скомпилировать эту программу.

Листинг 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();

}

Приложение А

Приоритеты операторов

Важно понять, что операторы имеют приоритеты, но запоминать их совсем не обязательно.

Приоритет оператора определяет последовательность, в которой программа выполняет операторы в выражении или формуле. Если один оператор имеет приоритет над другим оператором, то он выполняется первым.

Приоритет оператора убывает с увеличением номера категории. Все операторы одной категории имеют равный приоритет. Унарные операторы (категория 3), условный оператор (категория 14) и операторы присваивания (категория 15) ассоциируются справа налево, все остальные — слева направо. В приведенной ниже таблице операторы перечислены по категориям в порядке убывания их приоритетности.

Категория: 1 (Наивысшего приоритета)

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

Оператор: :: []

Категория: 2

Название или действие: Прямое и косвенное обращение к члену класса

Оператор: . ->

Название или действие: Вызов функции

Оператор: ()

Название или действие: Постфиксные инкремент и декремент

Оператор: ++ --

Ктегория: 3 (унарные)

Название или действие: Префиксные инкремент и декремент

Оператор: ++ --

Название или действие: Размер

Оператор: sizeof, sizeof()

Название или действие: Дополнение до единицы и логическое отрицание

Оператор: ^ !

Название или действие: Унарные минус и плюс

Оператор: - +

Название или действие: Получение адреса и разыменование

Оператор: ? *

Название или действие: оздание и удаление динамического объекта

Оператор: new, new[], delete, delete[]

Название или действие: Приведение типа

Оператор: casting

Категория: 4 (мультипликтивные)

Название или действие: Умножение, деление, деление по модулю

Оператор: * / %

Категория: 5 (аддитивные)

Название или действие: Бинарный плюс, бинарный минус

Оператор: + -

Категория: 6 (сдвига)

Название или действие: Вывода и ввода

Оператор: <<, >>

Категория: 7 (отношения)

Название или действие: Меньше, меньше или равно, больше, больше или равно

Оператор: <, <=, >, =>

Категория: 8 (равенства)

Название или действие: Равно, не равно

Оператор: ==, !=

Категория: 9

Название или действие: Побитовое И

Оператор: &

Категория: 10

Название или действие: Побитовое исключающее ИЛИ

Оператор: ^

Категория: 11

Название или действие: Побитовое ИЛИ

Оператор: |

Категория: 12

Название или действие: Логическое И

Оператор: &&

Категория: 13

Название или действие: Логическое ИЛИ

Оператор: ||

Категория: 14

Название или действие: Условный

Оператор: ?:

Категория: 15 (присваивания)

Название или действие: Простое присваивание

Оператор: =

Название или действие: Присваивание с умножением и делением

Оператор: *= /=

Название или действие: Присваивание с делением по модулю

Оператор: %=

Название или действие: Присваивание с суммой и разностью

Оператор: += -=

Название или действие: Присваивание со сдвигом

Оператор: <<= >>=

Название или действие: Присваивание с побитовым И, включающим ИЛИ и исключающим ИЛИ

Оператор: &= |= ^=

Название или действие: Генерация исключения

Оператор: throw

Название или действие: Запятая

Оператор: ,

Приложение Б

Ключевые слова C++

Ключевые слова зарезервированы в компиляторе как элементы языка программирования. Эти ключевые слова нельзя использовать в качестве имен при определении классов, переменных или функций. Приведенный список нельзя назвать абсолютно строгим, поскольку некоторые ключевые слова зависят от конкретного компилятора. Поэтому список ключевых слов вашего компилятора может быть немного другим.

auto, break, case, catch, char, class, const, continue, default, delete, do, double, else, enum, extern, float, for, friend, goto, if, int, long, mutable, new, operator, private, protected, public, register, return, short, signed, sizeof, static, struct, switch, template, this, throw, typedef, union, unsigned, virtual, void, volatile, while

Приложение В

Двоичные о числа

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

Понимание двоичных и шестнадцатеричных чисел потребует по-новому взглянуть на число 145 и увидеть в нем не число, а некоторый код для его выражения,

Начнем с малого. Рассмотрим взаимоотношения между числом три и символом "3". Символ числа (цифра) 3 — это некая "закорючка" на листе бумаги, число три — это некоторая идея или понятие. Определенная цифра используется для представления определенного числа.

Отличие между идеей и символом становится яснее, если осознавать, что для представления одного и того же понятия могут использоваться совершенно разные символы: три, 3, | | |, III или ***.

В десятичной системе счисления для представления чисел используются цифры 0, 1, 2, 3, 4, 5, 6, 7, 8 и 9. Как же представляется число десять?

Здесь возможны разные варианты. Можно было бы для представления этого понятия использовать букву А или "сороконожку" IIIIIIIIII, Римляне использовали символ X. В арабской системе (которой придерживаемся и мы) для представления числовых значений играет роль комбинация базовых десяти цифр. Первая (самая крайняя) позиция, или порядок, используется для единиц, а расположенная слева от нее — для десятков. Следовательно, число пятнадцать представляется как 15 (читается как "один, пять" ), т.е. 1 десяток и 5 единиц.

Итак, вырисовываются некоторые правила, позволяющие сделать ряд обобщений.

1. Для представления чисел по основанию 10 используются цифры 0-9.

2. Порядок представляет собой степень числа десять: единицы (1), десятки (10), сотни (100) и т.д.

3. Поскольку третья позиция в числе представляет сотни, то самым большим двузначным числом может быть 99. В общем случае, используя n позиций, можно представить числа от 0 до (Юп-1). Следовательно, с помощью трех позиций можно представить числа от 0 до (103-1), или 0-999.

Другие системы счисления

Отнюдь не случайно мы используем основание 10 — вспомните, ведь у нас на руках 10 пальцев. Однако вполне можно представить арифметику с использованием другого основания. Применяя правила, сформулированные для основания 10, можно описать представление чисел в системе счисления с основанием 8.

1. Для представления чисел по основанию 8 используются цифры 0-7.

2. Позиции разных порядков представляют собой степени числа восемь: единицы (1), восьмерки (8), 64-ки и т.д.

3. Используя n позиций, можно представить числа от 0 до (8n-1).

Чтобы различать числа, написанные с использованием разных оснований, это основание записывают рядом с числом как нижний индекс. Тогда число пятнадцать по основанию 10 следует записать как 15(10) и читать как "один, пять по основанию десять".

Таким образом, для представления числа 15(10) по основанию 8 следует записать 17(8). Это читается как "один, семь по основанию восемь". Обратите внимание, что это число также можно прочитать как "пятнадцать", поскольку именно его мы и имеем в виду, просто используем другое обозначение.

Откуда взялось число 17? Цифра 1 означает одну восьмерку, а цифра 7 означает 7 единиц. Одна восьмерка плюс семь единиц равно пятнадцати. Рассмотрим пятнадцать

звездочек: ***** *****

*****

Наше естественное желание — создать две группы: одна содержит десять звездочек, а другая — пять. В десятичной системе эта "композиция" представляется числом

15 (1 десяток и 5 единиц). Но те же звездочки можно сгруппировать и по-другому:

**** *******

****

т.е. имеем две группы: с восемью и семью звездочками. Такое распределение звездочек может служить иллюстрацией представления числа 17(8) с использованием основания восемь (одна восьмерка и семь единиц),

Еще об основаниях

Число пятнадцать по основанию десять представляется как 15, по основанию девять — как 16(9), no основанию восемь — как 17(8), а по основанию семь — как 21(7). В системе счисления по основанию 7 нет цифры 8, поэтому для представления числа пятнадцать нужно использовать две семерки и одну единицу.

Как же прийти к какому-нибудь общему принципу? Чтобы преобразовать десятичное число в число с основанием 7, вспомните о значении каждой порядковой позиции. В семеричной системе счисления переход к следующему порядку будет происходить на значениях, соответствующих десятичным числам: единица, семь, сорок девять, триста сорок три и т.д. Откуда взялись эти числа? Так ведь это же степени числа семь: 7^0, 7^0, 7^2, 7^3 и т.д. Построим следующую таблицу:

4 3 2 1

7^3 7^2 7^1 7^0

343 49 7 1

В первой строке представлен порядок числа. Во второй — степень числа семь, а в третьей — десятичное представление соответствующей степени числа семь.

Чтобы получить представление некоторого десятичного числа в системе счисления с основанием 7, выполните следующую процедуру. Проанализируйте, к числам какого порядка может относиться это значение. Возьмем, к примеру, число 200. Вы уже

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

Чтобы узнать, сколько раз число 49 (граничное значение третьего порядка) "поместится" в нашем числе, разделите его на 49. В ответе получается число 4, поэтому поставьте 4 в третью позицию и рассмотрите остаток, который в данном случае тоже равен 4. Поскольку в этом остатке не укладывается ни одной целой семерки, то во второй разряд (второй порядок) помещаем цифру 0. Нетрудно догадаться, что в остатке 4 содержится 4 единицы, поэтому и ставим цифру 4 в первую позицию (порядок единиц). В итоге получаем число 404(7).

Для преобразования числа 968 в систему счисления по основанию 6 используем следующую таблицу:

5 4 3 2 1

6^4 6^3 6^2 6^1 6^0

1296 216 36 6 1

В числе 968 число 1296 (граничное значение пятого порядка) не умещается ни разу, поэтому мы имеем дело с числом четвертого порядка. При делении числа 968 на число 216 (граничное значение четвертого порядка) получается число 4 с остатком, равным 104. В четвертую позицию ставим цифру 4. Делим остаток 104 на число 36 (граничное значение третьего порядка). Получаем в результате деления число 2 и остаток 32. Поэтому третья позиция будет содержать цифру 2. При делении остатка 32 на число 6 (граничное значение второго порядка) получаем 5 и остаток 2. Итак, в ответе имеем число 4252(6), что наглядно показано в следующей таблице:

5 4 3 2 1

6^4 6^3 6^2 6^1 6^0

1296 216 36 6 1

0 4 2 5 2

Для обратного преобразования, т.е. из системы счисления с недесятичным основанием (например, с основанием 6) в десятичную систему, достаточно умножить каждую цифру числа на граничное значение соответствующего порядка, а затем сложить полученные произведения:

4 * 216 864

2 * 36 = 72

5 * 6 = 30

2 * 1 = 2

sum = 968

Двоичная система счисления

Минимальным допустимым основанием является 2. В этом случае используются только две цифры: 0 и 1. Вот как выглядят порядки двоичного числа:

Порядок 8 7 6 5 4 3 2 1

Степень 2^7 2^6 2^5 2^4 2^3 2^2 2^1 2^0

Значение 128 64 32 16 8 4 2 1

Для преобразования числа 88 в двоичное число (с основанием 2) выполните описанную выше процедуру. В числе 88 число 128 не укладывается ни разу, поэтому в восьмой позиции ставим 0.

В числе 88 число 64 укладывается только один раз, поэтому в седьмую позицию ставим 1, а остаток равен 24. В числе 24 число 32 не укладывается ни разу, поэтому шестая позиция содержит 0.

В числе 24 число 16 укладывается один раз, поэтому пятой цифрой двоичного числа будет 1. Остаток при этом равен 8. В остатке 8 число 8 (граничное значение четвертого порядка) укладывается один раз, следовательно, в четвертой позиции ставим 1. Новый остаток равен нулю, поэтому в оставшихся позициях будут стоять нули.

0 1 0 1 1 0 0 0

Чтобы протестировать полученный ответ, выполним обратное преобразование:

1 * 64 = 64

0 * 32 = 0

1 * 16 = 16

1 * 8 = 8

0 * 4 = 0

0 * 2 = 0

0 * 1 = 0

sum = 88

Почему именно основание 2

Система счисления с основанием 2 более всего соответствует способу представления информации в компьютере. На самом деле компьютеры "понятия не имеют" ни о каких буквах, цифрах, командах или программах, поскольку представляют собой обычные электронные схемы, д

Наши рекомендации