- •Ста: Лекция №1 - Введение. Данные в памяти программ
- •Введение
- •Модель памяти в прикладных программах
- •Intmain ()
- •Int main ()
- •Int main ()
- •Int main ()
- •Char buf[ 2000000 ];
- •Return f();// бесконечно долго вызываем сами себя // вызываем переполнение стека через время
- •Int main ()
- •Return f();
- •Int main ()
- •Delete[]p;// Освобождаем память
- •Int main ()
- •Int a[ n ];// ошибка, размер нельзя вычислить во время компиляции
- •Delete[]p;
- •Int main ()
- •Проблема фиксированного размера массивов
- •Intmain ()
- •Int main ()
- •Int main ()
- •Динамически растущие массивы (векторы)
- •Int main ()
- •Struct IntegerVector
- •Int * m_pData;
- •Int m_nUsed;
- •Int m_nAllocated;
- •Void IntegerVectorDestroy ( IntegerVector & _vector )
- •#Ifndef_integer_vector_hpp_
- •#Include"integer_vector.Hpp"
- •Void IntegerVectorRead ( IntegerVector & _vector, std::istream & _stream );
Int main ()
{
// Приглашение пользователя ко вводу
std::cout << "Input integers, stop with Ctrl+Z: ";
// Инициализация динамически растущего массива:
// - выделение 10 начальных ячеек
// - счетчик занятых ячеек приравнивается к 0
// - счетчик выделенных ячеек равен 10
const int INITIAL_ALLOCATION_SIZE = 10;
int * pData = new int[ INITIAL_ALLOCATION_SIZE ];
int nUsed = 0, nAllocated = INITIAL_ALLOCATION_SIZE;
// Пытаемся вводить числа с консоли одно за другим
while (true )
{
// Попытка ввода числа
int temp;
std::cin >> temp;
if ( std::cin )
{
// Проверяем достаточно ли места в массиве
if ( nUsed == nAllocated )
{
// Места недостаточно, необходимо расширить массив.
// Выделяем новый блок вдвое больше существующего
int nAllocatedNew = nAllocated * 2;
int * pNewData = new int[ nAllocatedNew ];
// Переносим данные и существующего блока в новый
memcpy( pNewData, pData, sizeof( int ) * nAllocated );
// Освобождаем существующий блок и подменяем его адрес
delete[] pData;
pData = pNewData;
// Обновляем количество выделенных ячеек на новое
nAllocated = nAllocatedNew;
}
// Записываем данные в массив, точно зная, что место есть
pData[nUsed++ ] = temp;
}
else
// Конец ввода
break;
}
// Количество введенных данных сохранено в переменной nUsed
// Выводим данные в обратном порядке
for (int i =nUsed- 1; i >= 0; i-- )
std::cout << pData[ i ] << ' ';
std::cout << std::endl;
// Освобождаем последний выделенный блок данных
delete[]pData;
}

Новая версия программы заметно сложнее, тем не менее, она справляется с любым реальным объемом входных данных, при этом корректно завершается и рационально расходует память. Для 51 числа процедура расширения выполнится 3 раза (10->20, 20->40, 40->80), в последнем блоке останется 29 свободных ячеек.
Рефакторинг решения
Предложенное выше решение задачи удовлетворило выдвинутым требованиям, однако количество и сложность внесенного программного когда, необходимого для поддержания структуры динамически растущего массива, заметно превысили размеры самой задачи. Фактически, разглядеть контуры решения основной изначальной задачи в таком громоздком коде стало затруднительно. Желательно отделять код обеспечения структур данных от кода основной задачи для улучшения восприятия при чтении. Важен ли данный фактор? Несомненно, ведь профессиональный программист в реальных проектах читает существующий код в 5-10 раз чаще, чем пишет новый. а значит чтение должно даваться легко.
Еще одной побудительной причиной отделения может стать необходимость повторного использования структуры данных в других задачах. Ведь необходимость хранения последовательности чисел заранее неизвестной длины - типичная потребность для многих программ. Применим методы РЕФАКТОРИНГА - процесса изменения исходного кода программы с целью улучшения его внутренней структуры без влияния на внешне наблюдаемую функциональность.
Данные динамически растущего массива следует выделить в отдельную структуру, поскольку вместе они образуют целостное понятие и всегда будут существовать рядом друг с другом:
