Выбрать главу

// вернуть указатель на первый элемент

return (Account*)p;

}

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

char *p = new char[sizeof(Account)*elems];

Далее программа в цикле обходит этот блок, присваивая на каждой итерации переменной p адрес следующего элемента и вызывая либо конструктор с двумя параметрами, если задана пара начальных значений, либо конструктор по умолчанию:

for ( int ix = 0; ix

В разделе 14.3 говорилось, что оператор размещения new позволяет применить конструктор класса к уже выделенной области памяти. В данном случае мы используем new для поочередного применения конструктора класса Account к каждому из выделенных элементов массива. Поскольку при создании инициализированного массива мы подменили стандартный механизм выделения памяти, то должны сами позаботиться о ее освобождении. Оператор delete работать не будет:

delete [] ps;

Почему? Потому что ps (мы предполагаем, что эта переменная была инициализирована вызовом init_heap_array()) указывает на блок памяти, полученный не с помощью стандартного оператора new, поэтому число элементов в массиве компилятору неизвестно. Так что всю работу придется сделать самим:

void

Account::

dealloc_heap_array( Account *ps, size_t elems )

{

for ( int ix = 0; ix elems; ++ix )

ps[ix].Account::~Account();

delete [] reinterpret_castchar*(ps);

}

Если в функции инициализации мы пользовались арифметическими операциями над указателями для доступа к элементам:

new( p+offset*ix ) Account;

то здесь мы обращаемся к ним, задавая индекс в массиве ps:

ps[ix].Account::~Account();

Хотя и ps, и p адресуют одну и ту же область памяти, ps объявлен как указатель на объект класса Account, а p - как указатель на char. Индексирование p дало бы ix-й байт, а не ix-й объект класса Account. Поскольку с p ассоциирован не тот тип, что нужно, арифметические операции над указателями приходится программировать самостоятельно.

Мы объявляем обе функции статическими членами класса:

typedef pairchar*, double value_pair;

class Account {

public:

// ...

static Account* init_heap_array(

vector value_pair &init_values,

vector value_pair ::size_type elem_count = 0 );

static void dealloc_heap_array( Account*, size_t );

// ...

};

14.4.2. Вектор объектов

Когда определяется вектор из пяти объектов класса, например:

vector Point vec( 5 );

* то инициализация элементов производится в следующем порядке5: С помощью конструктора по умолчанию создается временный объект типа класса, хранящегося в векторе.

* К каждому элементу вектора применяется копирующий конструктор, в результате чего каждый объект инициализируется копией временного объекта.Временный объект уничтожается.

Хотя конечный результат оказывается таким же, как при определении массива из пяти объектов класса:

Point pa[ 5 ];

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

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

vector Point cvs; // пустой

int cv_cnt = calc_control_vertices();

// зарезервировать память для хранения cv_cnt объектов класса Point

// cvs все еще пуст ...

cvs.reserve( cv_cnt );

// открыть файл и подготовиться к чтению из него

ifstream infile( "spriteModel" );

istream_iterator Point cvfile( infile ),eos;

// вот теперь можно вставлять элементы

copy( cvfile, eos, inserter( cvs, cvs.begin() ));

(Алгоритм copy(), итератор вставки inserter и потоковый итератор чтения istream_iterator рассматривались в главе 12.) Поведение объектов list (список) и deque (двусторонняя очередь) аналогично поведению объектов vector (векторов). Вставка объекта в любой из этих контейнеров осуществляется с помощью копирующего конструктора.