Бьерн Страуструп.
Язык программирования С++
88
он не станет ненужным. В
частности, бывает удобно создать объект, который можно использовать
после возврата из функции, где он был создан. Подобные объекты создает операция 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 должна
Бьерн Страуструп.
Язык программирования С++
89
уметь определять размер размещенного объекта. Например:
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 - беззнаковый целочисленный тип, определенный в
.
Стандартная реализация функции operator new() не инициализирует предоставляемую память.
Что случится, когда операция new не сможет больше найти свободной памяти для размещения?
Поскольку даже виртуальная память небесконечна, такое время от времени происходит. Так, запрос
вида:
char* p = new char [100000000];
обычно не проходит нормально. Когда операция new не может выполнить запрос, она вызывает
функцию, которая была задана как параметр при обращении к функции set_new_handler() из .
Например, в следующей программе:
#include
#include
#include
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
main()
Бьерн Страуструп.
Язык программирования С++
90
{
char* p = new char[100000000];
cout << "done, p = " << long(p) << '\n';
}
выдаст
done, p = 0
Память не выделена, и вам сделано предупреждение! Отметим, что, задав реакцию на такую ситуацию
в функции new_handler, пользователь берет на себя проверку: исчерпана ли свободная память. Она
должна выполняться при каждом обращении в
программе к new (если только пользователь не
определил собственные функции для размещения объектов пользовательских типов; см.$$R.5.5.6).
Достарыңызбен бөлісу: