}
Заданный пользователем размер вектора игнорируется за исключением
некоторых типов, определяемых пользователем (#5.5.5).
Операции свободной памяти реализуются функциями (#с.7.2.3):
void operator new(long);
void operator delete(void*);
- стр 99 -
Стандартная реализация new не инициализирует возвращаемый объект.
Что происходит, когда new не находит памяти для выделения?
Поскольку даже виртуальная память конечна, это иногда должно
происходить. Запрос вроде
char* p = new char[100000000];
как правило, приводит к каким-то неприятностям. Когда у new ничего
не получается, она вызывает функцию, указываемую указаетелем
_new_handler (указатели на функции обсуждаются в #4.6.9). Вы можете
задать указатель явно или использовать функцию set_new_handler().
Например:
#include
void out_of_store()
{
cerr << "операция new не прошла: за пределами памяти\n";
exit(1);
}
typedef void (*PF)(); // тип указатель на функцию
extern PF set_new_handler(PF);
main()
{
set_new_handler(out_of_store);
char* p = new char[100000000];
cout << "сделано, p = " << long(p) << "\n";
}
как правило, не будет писать "сделано", а будет вместо этого
выдавать
операция new не прошла: за пределами памяти
_new_handler может делать и кое-что поумнее, чем просто завершать
выполнение программы. Если вы знаете, как работают new и delete,
например, потому, что вы задали свои собственные operator new() и
operator delete(), программа обработки может попытаться найти
некоторое количество памяти, которое возвратит new. Другими
словами, пользователь может сделать сборщик мусора, сделав, таким
образом, использование delete необязательным. Но это, конечно, все-
таки задача не для начинающего.
По историческим причинам new просто возвращает указатель 0, если
она не может найти достаточное количество памяти и не был задан
никакой _new_handler. Например
include
main()
{
char* p = new char[100000000];
cout << "сделано, p = " << long(p) << "\n";
}
- стр 100 -
выдаст
сделано, p = 0
Вам сделали предупреждение! Заметьте, что тот, кто задает
_new_handler, берет на себя заботу по проверке истощения памяти при
каждом использовании new в програме (за исключением случая, когда
пользователь задал отдельные подпрограммы для размещения объектов
заданных типов, определяемых пользователем; см. #5.5.6).
3.3 Сводка операторов
Операторы C++ систематически и полностью изложены в #с.9,
прочитайте, пожалуйста, этот раздел. А здесь приводится краткая
сводка и некоторые примеры.
Синтаксис оператора
---------------------------------------------------------------
-----
оператор:
описание
{список_операторов opt}
выражение opt
if ( выражение ) опреатор
if ( выражение ) оператор else оператор
switch ( выражение ) оператор
while ( выражение ) оператор
do оператор while (выражение)
for ( оператор выражение opt ; выражение opt ) оператор
case константное_выражение : оператор
default : оператор
break ;
continue ;
return выражение opt ;
goto идентификатор ;
идентификатор : оператор
список_операторов:
оператор
оператор список_операторов
Заметьте, что описание является оператором, и что нет операторов
присваивания и вызова процедуры. Присваивание и вызов функции
обрабатываются как выражения.
- стр 101 -
3.3.1 Проверки
Проверка значения может осуществляться или оператором if, или
оператором switch:
if ( выражение ) оператор
if ( выражение ) оператор else оператор
switch ( выражение ) оператор
В C++ нет отдельного булевского типа. Операции сравнения
== != < <= > >=
возвращают целое 1, если сравнение истинно, иначе возвращают 0. Не
так уж непривычно видеть, что ИСТИНА определена как 1, а ЛОЖЬ
определена как 0.
В операторе if первый (или единственный) оператор выполняется в
том случае, если выражение ненулевое, иначе выполняется второй
оператор (если он задан). Отсюда следует, что в качестве условия
может использоваться любое целое выражение. В частности, если a
целое, то
if (a) // ...
эквивалентно
if (a != 0) // ...
Логические операции
&& || !
наиболее часто используются в условиях. Операции && и || не будут
вычислять второй аргумент, если это ненужно. Например:
if (p && 1count) // ...
вначале проверяет, является ли p не нулем, и только если это так,
то проверяет 1count.
Некоторые простые операторы if могут быть с удобством заменены
выражениями арифметического if. Например:
if (a <= d)
max = b;
else
max = a;
лучше выражается так:
max = (a<=b) ? b : a;
Скобки вокруг условия необязательны, но я считаю, что когда они
используются, программу легче читать.
Некоторые простые операторы switch можно по-другому записать в
виде набора операторов if. Например:
- стр 102 -
switch (val) {
case 1:
f();
break;
case 2;
g();
break;
default:
h();
break;
}
иначе можно было бы записать так:
if (val == 1)
f();
else if (val == 2)
g();
else
h();
Смысл тот же, однако первый вариант (switch) предпочтительнее,
поскольку в этом случае явно выражается сущность действия
(сопоставление значения с рядом констант). Поэтому в нетривиальных
случаях оператор switch читается легче.
Заботьтесь о том, что switch должен как-то завершаться, если
только вы не хотите, чтобы выполнялся следующий case. Например:
switch (val) { // осторожно
case 1:
cout << "case 1\n";
case 2;
cout << "case 2\n";
default:
cout << "default: case не найден\n";
}
при val==1 напечатает
case 1
case 2
default: case не найден
к великому изумлению непосвященного. Самый обычный способ завершить
случай - это break, иногда можно даже использовать goto. Например:
- стр 103 -
switch (val) { // осторожно
case 0:
cout << "case 0\n";
case1:
case 1:
cout << "case 1\n";
return;
case 2;
cout << "case 2\n";
goto case1;
default:
cout << "default: case не найден\n";
return;
}
При обращении к нему с val==2 выдаст
case 2
case 1
Заметьте, что метка case не подходит как метка для употребления в
операторе goto:
goto case 1; // синтаксическая ошибка
3.3.2 Gotо
C++ снабжен имеющим дурную репутацию оператором goto.
goto идентификатор;
идентификатор : оператор
В общем, в программировании высокого уровня он имеет очень мало
применений, но он может быть очень полезен, когда C++ программа
генерируется программой, а не пишется непосредственно человеком.
Например, операторы goto можно использовать в синтаксическом
анализаторе, порождаемом генератором синтаксических анализаторов.
Оператор goto может быть также важен в тех редких случаях, когда
важна наилучшая эффективность, например, во внутреннем цикле какой-
нибудь программы, работающей в реальном времени.
Одно из немногих разумных применений состоит в выходе из
вложенного цикла или переключателя (break лишь прекращает
выполнение самого внутреннего охватывающего его цикла или
переключателя). Например:
for (int i = 0; i
3.4 Комментарии и Выравнивание
Продуманное использование комментариев и согласованное
использование отступов может сделать чтение и понимание программы
намного более приятным. Существует несколько различных стилей
согласованного использования отступов. Автор не видит никаких
серьезных оснований предпочесть один другому (хотя как и у
большинства, у меня есть свои предпочтения). Сказанное относится
также и к стилю комментариев.
Неправильное использование комментариев может серьезно повлиять
на удобочитаемость программы, Компилятор не понимает содержание
комментария, поэтому он никаким способом не может убедиться в том,
что комментарий
[1] осмыслен;
[2] описывает программу; и
[3] не устарел.
Непонятные, двусмыленные и просто неправильные комментарии
содержатся в большинстве программ. Плохой комментарий может быть
хуже, чем никакой.
Если что-то можно сформулировать средствами самого языка, следует
это сделать, а не просто отметить в комментарии. Данное замечание
относится к комментариям вроде:
// переменная "v" должна быть инициализирована.
// переменная "v" должна использоваться только функцией "f()".
// вызвать функцию init() перед вызовом
// любой другой функции в этом файле.
// вызовите функцию очиститки "cleanup()" в конце вашей
программы.
// не используйте функцию "wierd()".
// функция "f()" получает два параметра.
При правильном использовании C++ подобные комментарии как правило
становятся ненужными. Чтобы предыдущие комментарии стали излишними,
можно, например, использовать правила компоновки (#4.2) и
видимость, инициализацию и правила очистки для классов (см.
#5.5.2).
Если что-то было ясно сформулировано на языке, второй раз
упоминать это в комментарии не следует. Например:
a = b+c; // a становится b+c
count++; // увеличить счетчик
Такие комментарии хуже чем просто излишни, они увеличивают объем
текса, который надо прочитать, они часто затуманивают структуру
программы, и они могут быть неправильными.
Автор предпочитает:
- стр 105 -
[1] Комментарий для каждого исходного файла, сообщающий, для чего
в целом предназначены находящиеся в нем комментарии, дающий
ссылки на справочники и руководства, общие рекомендации по
использованию и т.д.;
[2] Комментарий для каждой нетривиальной функции, в котором
сформулировано ее назначение, используемый алгоритм (если он
неочевиден) и, быть может, что-то о принимаемых в ней
предположениях относительно среды выполнения;
[3] Небольшое число комментариев в тех местах, где программа
неочевидна и/или непереносима; и
[4] Очень мало что еще.
Например:
// tbl.c: Реализация таблицы имен
/*
Гауссовское исключение с частичным
См. Ralston: "A first course ..." стр. 411.
*/
// swap() предполагает размещение стека AT&T sB20.
/**************************************
Copyright (c) 1984 AT&T, Inc.
All rights reserved