Скачать презентацию
Идет загрузка презентации. Пожалуйста, подождите
Презентация была опубликована 9 лет назад пользователемВасилий Ивановский
1 ПРОБЛЕМЫ ПРИ ЯВНОМ РАСПРЕДЕЛЕНИИ ПАМЯТИ В С++, СПОСОБЫ ИХ РЕШЕНИЯ. ССЫЛКИ И УКАЗАТЕЛИ. РАСПРЕДЕЛЕНИЕ ПАМЯТИ ПОД ПЕРЕМЕННЫЕ, УПРАВЛЕНИЕ ПАМЯТЬЮ С ПОМОЩЬЮ ПЕРЕОПРЕДЕЛЕНИЯ ОПЕРАТОРОВ NEW И DELETE. Распределение памяти
2 Автоматические переменные Самый простой метод – это объявление переменных внутри функций. Если переменная объявлена внутри функции, каждый раз, когда функция вызывается, под переменную автоматически отводится память. Когда функция завершается, память, занимаемая переменными, освобождается. Такие переменные называют автоматическими.
3 Автоматические переменные При создании автоматических переменных они никак не инициализируются, т.е. значение автоматической переменной сразу после ее создания не определено, и нельзя предсказать, каким будет это значение. Соответственно, перед использованием автоматических переменных необходимо либо явно инициализировать их, либо присвоить им какое- либо значение.
4 Пример int funct() { double f; // значение f не определено f = 1.2; // теперь значение f определено // явная инициализация автоматической // переменной bool result = true;... }
5 Автоматические переменные Аналогично автоматическим переменным, объявленным внутри функции, автоматические переменные, объявленные внутри блока (последовательности операторов, заключенных в фигурные скобки) создаются при входе в блок и уничтожаются при выходе из блока. Замечание. Распространенной ошибкой является использование адреса автоматической переменной после выхода из функции.
6 Пример int* func() { int x;... return &х; } дает непредсказуемый результат.
7 Статические переменные Другой способ выделения памяти – статический Если переменная определена вне функции, память для нее отводится статически, один раз в начале выполнения программы, и переменная уничтожается только тогда, когда выполнение программы завершается. Можно статически выделить память и под переменную, определенную внутри функции или блока. Для этого нужно использовать ключевое слово static в его определении:
8 Пример double globalMax; // переменная определена вне // функции void func(int x) { static bool visited = false; if (!visited) {... // инициализация visited = true; }
9 Статические переменные В данном примере переменная visited создается в начале выполнения программы. Ее начальное значение – false. При первом вызове функции funcусловие в операторе if будет истинным, выполнится инициализация, и переменной visited будет присвоено значение true. Поскольку статическая переменная создается только один раз, ее значения между вызовами функции сохраняются. При втором и последующих вызовах функции func инициализация производиться не будет. Если бы переменная visited не была объявлена static, то инициализация происходила бы при каждом вызове функции.
10 Динамическое выделение памяти Третий способ выделения памяти в языке С++ – динамический. Память для величины какого-либо типа можно выделить, выполнив операцию new. В качестве операнда выступает название типа, а результатом является адрес выделенной памяти.
11 Пример long* lp; //создать новое целое число lp = new long; Complex* cp; //создать новый объект Complex cp = new Complex; Созданный таким образом объект существует до тех пор, пока память не будет явно освобождена с помощью операции delete. В качестве операнда delete должен быть задан адрес, возвращенный операцией new: delete lp; delete cp;
12 Динамическое выделение памяти Динамическое распределение памяти используется, прежде всего, тогда, когда заранее неизвестно, сколько объектов понадобится в программе и понадобятся ли они вообще. С помощью динамического распределения памяти можно гибко управлять временем жизни объектов, например выделить память не в самом начале программы (как для глобальных переменных), но, тем не менее, сохранять нужные данные в этой памяти до конца программы.
13 Указатели Для большинства типов T указатель на T имеет тип T*. Это значит, что переменная типа T* может хранить адрес объекта типа T. Указатели на массивы и функции, к сожалению, требуют более сложной записи: int* pi; //указатель на целое число char** cpp; //указатель на указатель на char int (*vp)[10]; //массив из 10 чисел int (*fp)(char, char*); //указатель на функцию с параметрами //char и char*, возвращающую int
14 Указатели Главная операция над указателями - это косвенное обращение(разыменование), т.е. обращение к объекту, на который настроен указатель. Эту операцию обычно называют просто косвенностью. Операция косвенности * является префиксной унарной операцией.
15 Пример Например: char c1 = a; char* p = &c1; // p содержит адрес c1 char c2 = *p; // c2 = a Переменная, на которую указывает p - это c1, а значение, которое хранится в c1, равно 'a'. Поэтому присваиваемое c2 значение *p есть 'a'.
16 Динамический массив Если необходимо динамически создать массив, то нужно использовать немного другую форму new: new int[100]; В отличие от определения переменной типа массив, размер массива в операции new может быть произвольным, в том числе вычисляемым в ходе выполнения программы. (При объявлении переменной типа массив размер массива должен быть константой.) Освобождение памяти, выделенной под массив, должно быть выполнено с помощью следующей операции delete: delete [] address;
17 Выделение памяти под строки В следующем фрагменте программы мы динамически выделяем память под строку переменной длины и копируем туда исходную строку: // стандартная функция strlen подсчитывает // количество символов в строке int length = strlen(src_str); // выделить память и добавить один байт // для завершающего нулевого байта char* buffer = new char[length + 1]; strcpy(buffer, src_str); // копирование строки
18 Выделение памяти под строки Операция new возвращает адрес выделенной памяти. Однако нет никаких гарантий, что new обязательно завершится успешно. Объем оперативной памяти ограничен, и может случиться так, что найти еще один участок свободной памяти будет невозможно. В таком случае new возвращает нулевой указатель (адрес 0). Результат new необходимо проверять: char* newstr; size_t length = 4; newstr = new char[length]; if (newstr == NULL) { //проверить результат; обработка ошибок } // память выделена успешно
19 Рекомендации по использованию указателей и динамического распределения памяти Указатели и динамическое распределение памяти – очень мощные средства языка. С их помощью можно разрабатывать гибкие и весьма эффективные программы. В частности, одна из областей применения С++ – системное программирование – практически не могла бы существовать без возможности работы с указателями. Однако возможности, которые получает программист при работе с указателями, накладывают на него и большую ответственность. Наибольшее количество ошибок в программу вносится именно при работе с указателями. Как правило, эти ошибки являются наиболее трудными для обнаружения и исправления.
20 Некоторые ошибки Использование неверного адреса в операции delete. Результат такой операции непредсказуем. Вполне возможно, что сама операция пройдет успешно, однако внутренняя структура памяти будет испорчена, что приведет либо к ошибке в следующей операции new, либо к порче какой-нибудь информации. Пропущенное освобождение памяти, т.е. программа многократно выделяет память под данные, но "забывает" ее освобождать. Такие ошибки называют утечками памяти. Во-первых, программа использует ненужную ей память, тем самым понижая производительность. Запись по неверному адресу. Скорее всего, будут испорчены какие-либо данные. Как проявится такая ошибка – неверным результатом, сбоем программы или иным образом – предсказать трудно.
21 Общие рекомендации 1) Используйте указатели и динамическое распределение памяти только там, где это действительно необходимо. Проверьте, можно ли выделить память статически или использовать автоматическую переменную. 2) Старайтесь локализовать распределение памяти. Если какой-либо метод выделяет память (в особенности под временные данные), он же и должен ее освободить. 3) Там, где это возможно, вместо указателей используйте ссылки. 4) Проверяйте программы с помощью специальных средств контроля памяти.
22 Ссылки Ссылка – это еще одно имя переменной. Если имеется какая-либо переменная, например: Complex x; то можно определить ссылку на переменную x как: Complex& y = x; и тогда x и y обозначают одну и ту же величину. Если выполнены операторы x.real = 1; x.imaginary = 2; то y.real равно 1 и y.imaginary равно 2.
23 Ссылки Фактически, ссылка – это адрес переменной (поэтому при определении ссылки используется символ & -- знак операции взятия адреса), и в этом смысле она сходна с указателем, однако у ссылок есть свои особенности.
24 Особенности ссылок Во-первых, определяя переменную типа ссылки, ее необходимо инициализировать, указав, на какую переменную она ссылается. Нельзя определить ссылку int& xref; можно только int& xref = x;
25 Особенности ссылок Во-вторых, нельзя переопределить ссылку, т.е. изменить на какой объект она ссылается. Если после определения ссылки xref мы выполним присваивание xref = y; то выполнится присваивание значения переменной y той переменной, на которую ссылается xref. Ссылка xref по-прежнему будет ссылаться на x.
26 Особенности ссылок В результате выполнения следующего фрагмента программы: int x = 10; int y = 20; int& xref = x; xref = y; x += 2; cout << "x = " << x << endl; cout << "y = " << y << endl; cout << "xref = " << xref << endl; будет выведено: x = 22 y = 20 xref = 22.
27 Особенности ссылок В-третьих, синтаксически обращение к ссылке аналогично обращению к переменной. Если для обращения к атрибуту объекта, на который ссылается указатель, применяется операция ->, то для подобной же операции со ссылкой применяется точка ".". Complex a; Complex* aptr = &a; Complex& aref = a; aptr->real = 1; aref.imaginary = 2; real = 1; aref.imaginary = 2;">
28 Особенности ссылок Как и указатель, ссылка сама по себе не имеет значения! Ссылка должна на что-то ссылаться, тогда как указатель должен на что-то указывать.
29 Распределение памяти при передаче аргументов функции
Еще похожие презентации в нашем архиве:
© 2024 MyShared Inc.
All rights reserved.