Смекни!
smekni.com

Правила правой руки 17 Замечания для программистов на c 17 Глава 1 (стр. 19 из 43)

}

Заданный пользователем размер вектора игнорируется за исключением

некоторых типов, определяемых пользователем (#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 не прошла: за пределами памяти&bsol;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) << "&bsol;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) << "&bsol;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&bsol;n";

case 2;

cout << "case 2&bsol;n";

default:

cout << "default: case не найден&bsol;n";

}

при val==1 напечатает

case 1

case 2

default: case не найден

к великому изумлению непосвященного. Самый обычный способ завершить

случай - это break, иногда можно даже использовать goto. Например:

- стр 103 -

switch (val) { // осторожно

case 0:

cout << "case 0&bsol;n";

case1:

case 1:

cout << "case 1&bsol;n";

return;

case 2;

cout << "case 2&bsol;n";

goto case1;

default:

cout << "default: case не найден&bsol;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