Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
c_1_po_16.docx
Скачиваний:
5
Добавлен:
06.12.2018
Размер:
71.4 Кб
Скачать

Достоинства стековых объектов

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

Автоматическое удаление — второе большое преимущество стековых объектов, поэтому программисты часто создают маленькие вспомогательные стековые классы, которые играют роль «обертки» для динамических объектов. В следующем забавном примере динамический класс Foo «упаковывается» в стековый класс PFoo. Конструктор выделяет память для Foo; деструктор освобождает ее. Если вы незнакомы с операторами преобразования, обратитесь к соответствующему разделу этой главы. В двух словах, функция operator Foo*() позволяет использовать класс PFoo везде, где должен использоваться Foo* — например, при вызове функции g().

class PFoo {

private:

Foo* f;

public:

PFoo() : f(new Foo) {}

~PFoo() { delete f; }

operator Foo*() { return f; }

}

void g(Foo*);

{

PFoo p;

g(p); // Вызывает функцию operator Foo*() для преобразования

// Уничтожается p, а за ним – Foo

}

Обратите внимание, что этот класс не совсем безопасен, поскольку адрес, возвращаемый функцией operator Foo*(), становится недействительным после удаления вмещающего PFoo. Мы разберемся с этим чуть позже.

Динамические объекты тоже могут удаляться автоматически, но это достаточно сложно.

У стековых объектов есть еще одно преимущество — если ваш компилятор поддерживает ANSI-совместимую обработку исключений (exception). Когда во время раскрутки стека происходит исключение, деструкторы стековых объектов вызываются автоматически. Для динамических объектов это не работает.

11) Указатели, арифметика указателей.

Каждая переменная (как и лю­бой другой программный объект) имеет свой адрес - т.е. координату места ее разме­щения в памяти. Память представляется как простая последовательность байтов, пронумерованных целыми положительными числами. Под координатой объекта в памяти понимается номер первого байта этого объекта в указанной системе ну­мерации. Для определения адреса используется специальная унарная префиксная операция &. Выражение &a возвращает адрес переменной a. Для работы с адреса­ми объектов используются указатели. Указатели - это специальный вид объектов, существующих в C/C++ программах. Значение указателя - это адрес некоторого объекта. Для того, чтобы обеспечить доступ к объекту, указатель должен, кроме адреса объекта, содержать информацию о его размере и структуре. Как известно, такая информация содержится в типах данных. Исходя из этого, указатели объяв­ляются следующим образом:

<тип>* <указатель>

Например:

int *iptr; // Указатель на переменную типа int

char *cptr; // Указатель на переменную типа char

С указателями допустимы следующие операции:

  1. Присваивание значения одного указателя другому:

int *ptr1, *ptr2;

ptr1=ptr2;

  1. Присваивание указателю значения адреса объекта соответствующего типа:

int *ptr;

int x;

ptr = &a;

  1. Операции сравнения на равенство и неравенство.

  2. К указателям можно применять операции сложения с целым чис­лом. Эти операции работают своеобразно и дают мощные средства управления памятью. Выясним это на конкретном примере.

Пусть объявлен указатель dptr типа double* (значениями этого указателя мо­гут быть адреса объектов типа double). Напомним, что переменные типа double занимают в памяти 8 байт. В этой ситуации выражение dptr+k (где k - целое число) будет иметь тип double* (т.е. снова является указателем на объекты типа double) и значение, равное исходному значению переменной dptr плюс 8k. То есть указатель перемещается по памяти вперед на расстояние, равное размеру места, занимаемого в памяти k элементами типа, к которому отнесен указатель. Операция вычитания работает аналогично. С операциями сложения и вычитания связаны такие опера­ции, как ++, --, += и -=. Эти операции работают так же, как и операции сложения и вычитания.

  1. Разность двух указателей является целым числом. Это число имеет следующий смысл: на каком расстоянии друг от друга в памяти находятся объекты, адреса которых содержатся в указателях.

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

  3. Последняя операция, применимая к указателям, - это операция разыменования или получения значения объекта, адрес которого содержит указатель. Это опера­ция "унарная * ". Рассмотрим пример:

Пусть dptr - это указатель на объекты типа double и пусть в некоторый момент времени этот указатель содержит адрес какого-то реально су­ществующего в памяти объекта типа double (например, ранее описана переменная x типа double и указателю dptr присвоен адрес этой переменной). Синтаксически *dptr представляет собой выражение. Значение этого выражения - ссылка на объ­ект, адрес которого содержится в переменной dptr. Это выражение может исполь­зоваться в других выражениях всюду, где допустимо использование переменной соответствующего типа (в том числе и в левой части выражения присваивания). Например, выражение *dptr=12.7 обрабатывается так: в объект типа double, рас­положенный по адресу, находящемуся в переменной dptr записывается значение 12.7, которое становится значением всего выражения присваивания.

Значение выражения *(dptr+6) определяется следующим образом: область па­мяти, начинающаяся с адреса, значение которого равно значению переменной (указателя) dptr плюс 48 (48=8х6) и занимающая в памяти 8 байт, трактуется как объект типа double, ссылка на который и становится значением всего выра­жения.

Пока неясно, для чего необходимо применять указатели.

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

На работе с указателями построена вся работа с массивами.

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]