Б. Страуструп - Язык программирования С++ (1119446), страница 28
Текст из файла (страница 28)
В лучшем случае подобная программа будет переносимой. Обычно без особого рискаможно предположить, что указатели на различные структуры имеют одинаковое представление. Далее,произвольный указатель можно присвоить (без явного преобразования типа) указателю типа void*, аvoid* может быть явно преобразован обратно в указатель произвольного типа.В языке С++ явные преобразования типа оказывается излишними во многих случаях, когда в С (идругих языках) они требуются. Во многих программах можно вообще обойтись без явныхпреобразований типа, а во многих других они могут быть локализованы в нескольких подпрограммах.3.2.6 Свободная памятьИменованный объект является либо статическим, либо автоматическим (см.$$2.1.3).
Статическийобъект размещается в памяти в момент запуска программы и существует там до ее завершения.Автоматический объект размещается в памяти всякий раз, когда управление попадает в блок,содержащий определение объекта, и существует только до тех пор, пока управление остается в этомблоке. Тем не менее, часто бывает удобно создать новый объект, который существует до тех пор, пока87Бьерн Страуструп.Язык программирования С++он не станет ненужным. В частности, бывает удобно создать объект, который можно использоватьпосле возврата из функции, где он был создан. Подобные объекты создает операция new, а операцияdelete используется для их уничтожения в дальнейшем.
Про объекты, созданные операцией new,говорят, что они размещаются в свободной памяти. Примерами таких объектов являются узлыдеревьев или элементы списка, которые входят в структуры данных, размер которых на этапетрансляции неизвестен. Давайте рассмотрим в качестве примера набросок транслятора, которыйстроится аналогично программе калькулятора. Функции синтаксического анализа создают изпредставлений выражений дерево, которое будет в дальнейшем использоваться для генерации кода.Например:struct enode {token_value oper;enode* left;enode* right;};enode* expr(){enode* left = term();for(;;)switch(curr_tok) {case PLUS:case MINUS:get_token();enode* n = new enode;n->oper = curr_tok;n->left = left;n->right = term();left = n;break;default:return left;}}Генератор кода может использовать дерево выражений, например так:void generate(enode* n){switch (n->oper) {case PLUS:// соответствующая генерацияdelete n;}}Объект, созданный с помощью операции new, существует, до тех пор, пока он не будет явно уничтоженоперацией delete.
После этого память, которую он занимал, вновь может использоваться new. Обычнонет никакого "сборщика мусора", ищущего объекты, на которые никто не ссылается, ипредоставляющего занимаемую ими память операции new для повторного использования. Операндомdelete может быть только указатель, который возвращает операция new, или нуль. Применение delete кнулю не приводит ни к каким действиям.Операция new может также создавать массивы объектов, например:char* save_string(const char* p){char* s = new char[strlen(p)+1];strcpy(s,p);return s;}Отметим, что для перераспределения памяти, отведенной операцией new, операция delete должна88Бьерн Страуструп.Язык программирования С++уметь определять размер размещенного объекта. Например:int main(int argc, char* argv[]){if (argc < 2) exit(1);char* p = save_string(arg[1]);delete[] p;}Чтобы добиться этого, приходится под объект, размещаемый стандартной операцией new, отводитьнемного больше памяти, чем под статический (обычно, больше на одно слово).
Простой оператор deleteуничтожает отдельные объекты, а операция delete[] используется для уничтожения массивов.Операции со свободной памятью реализуются функциями ($$R.5.3.3-4):void* operator new(size_t);void operator delete(void*);Здесь size_t - беззнаковый целочисленный тип, определенный в <stddef.h>.Стандартная реализация функции operator new() не инициализирует предоставляемую память.Что случится, когда операция new не сможет больше найти свободной памяти для размещения?Поскольку даже виртуальная память небесконечна, такое время от времени происходит. Так, запросвида:char* p = new char [100000000];обычно не проходит нормально.
Когда операция new не может выполнить запрос, она вызываетфункцию, которая была задана как параметр при обращении к функции set_new_handler() из <new.h>.Например, в следующей программе:#include <iostream.h>#include <new.h>#include <stdlib.h>void out_of_store(){cerr << "operator new failed: out of store\n";exit(1);}int main(){set_new_handler(&out_of_store);char* p = new char[100000000];cout << "done, p = " << long(p) << '\n';}скорее всего, будет напечатано не "done", а сообщение:operator new failed: out of store// операция new не прошла: нет памятиС помощью функции new_handler можно сделать нечто более сложное, чем просто завершитьпрограмму.
Если известен алгоритм операций new и delete (например, потому, что пользовательопределил свои функции operator new и operator delete), то обработчик new_handler может попытатьсянайти свободную память для new. Другими словами, пользователь может написать свой "сборщикмусора", тем самым сделав вызов операции delete необязательным. Однако такая задача, безусловно,не под силу новичку.По традиции операция new просто возвращает указатель 0, если не удалось найти достаточносвободной памяти. Реакция же на это new_handler не была установлена.
Например, следующаяпрограмма:#include <stream.h>main()89Бьерн Страуструп.Язык программирования С++{char* p = new char[100000000];cout << "done, p = " << long(p) << '\n';}выдастdone, p = 0Память не выделена, и вам сделано предупреждение! Отметим, что, задав реакцию на такую ситуациюв функции new_handler, пользователь берет на себя проверку: исчерпана ли свободная память.
Онадолжна выполняться при каждом обращении в программе к new (если только пользователь неопределил собственные функции для размещения объектов пользовательских типов; см.$$R.5.5.6).3.3 Сводка операторовПолное и последовательное описание операторов С++ содержится в $$R.6.
Советуем ознакомиться сэтим разделом. Здесь же дается сводка операторов и несколько примеров.Синтаксис операторовоператор:описание{ список-операторов opt }выражение opt ;if ( выражение ) операторif ( выражение ) оператор else операторswitch ( выражение ) операторwhile ( выражение ) операторdo оператор while ( выражение )for (начальный-оператор-for выражение opt; выражение opt) операторcase выражение-константа : операторdefault : операторbreak ;continue ;returnвыражение opt ;goto идентификатор ;идентификатор : операторсписок-операторов:операторсписок-операторовоператорначальный-оператор-for:описаниевыражение opt ;Обратите внимание, что описание является оператором, но нет операторов присваивания или вызовафункции (они относятся к выражениям).3.3.1 Выбирающие операторыЗначение можно проверить с помощью операторов if или switch:if ( выражение ) операторif ( выражение ) оператор elseswitch ( выражение ) оператороператор90Бьерн Страуструп.Язык программирования С++В языке С++ среди основных типов нет отдельного булевского (тип со значениями истина, ложь).
Всеоперации отношений:==!=<><=>=дают в результате целое 1, если отношение выполняется, и 0 в противном случае. Обычно определяютконстанты TRUE как 1 и FALSE как 0.В операторе if, если выражение имеет ненулевое значение, выполняется первый оператор, а иначевыполняется второй (если он указан). Таким образом, в качестве условия допускается любоевыражение типа целое или указатель.
Пусть a целое, тогдаif (a)// ...эквивалентноif (a != 0) ...Логические операции&&||!обычно используются в условиях. В операциях && и || второй операнд не вычисляется, если результатопределяется значением первого операнда. Например, в выраженииif (p && l<p->count)// ...сначала проверяется значение p, и только если оно не равно нулю, то проверяется отношение l<p>count.Некоторые простые операторы if удобно заменять выражениями условия. Например, вместооператораif (a <= b)max = b;elsemax = a;лучше использовать выражениеmax = (a<=b) ? b : a;Условие в выражении условия не обязательно окружать скобками, но если их использовать, товыражение становится понятнее.Простой переключатель (switch) можно записать с помощью серии операторов if.
Например,switch (val) {case 1:f();break;case 2:g();break;default:h();break;}можно эквивалентно задать так:if (val == 1)f();else if (val == 2)g();elseh();Смысл обеих конструкций совпадает, но все же первая предпочтительнее, поскольку в ней нагляднеепоказана суть операции: проверка на совпадение значения val со значением из множества констант.91Бьерн Страуструп.Язык программирования С++Поэтому в нетривиальных случаях запись, использующая переключатель, понятнее.Нужно позаботиться о каком-то завершении оператора, указанного в варианте переключателя, еслитолько вы не хотите, чтобы стали выполняться операторы из следующего варианта. Например,переключательswitch (val)case 1:cout <<case 2:cout <<default:cout <<}{// возможна ошибка"case 1\n";"case 2\n";"default: case not found\n";при val==1 напечатает к большому удивлению непосвященных:case 1case 2default: case not foundИмеет смысл отметить в комментариях те редкие случаи, когда стандартный переход на следующийвариант оставлен намеренно.