С++ для начинающих


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


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

vector< Point > vec( 5 );

то инициализация элементов производится в следующем порядке5:

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

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

3.      Временный объект уничтожается.

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

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 (векторов). Вставка объекта в любой из этих контейнеров осуществляется с помощью копирующего конструктора.


Упражнение 14.9

Какие из приведенных инструкций неверны? Исправьте их.

(a) Account *parray[10] = new Account[10];

(b) Account iA[1024] = {

    "Nhi", "Le", "Jon", "Mike", "Greg", "Brent", "Hank"

    "Roy", "Elena" };

(c) string *ps=string[5]("Tina","Tim","Chyuan","Mira","Mike");

(d) string as[] = *ps;

Упражнение 14.10

Что лучше применить в каждой из следующих ситуаций: статический массив (такой, как Account pA[10]), динамический массив или вектор? Объясните свой выбор.

Внутри функции Lut() нужен набор из 256 элементов для хранения объектов класса Color. Значения являются константами.

Необходимо хранить набор из неизвестного числа объектов класса Account. Данные счетов читаются из файла.

Функция gen_words(elem_size) должна сгенерировать и передать обработчику текста набор из elem_size строк.

Упражнение 14.11

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

// печально: не проверяется, что parray адресует массив

delete parray;

вместо

// правильно: определяется размер массива, адресуемого parray

delete [] parray;

Наличие пары скобок заставляет компилятор найти размер массива. Затем к каждому элементу по очереди применяется деструктор (всего size раз). Если же скобок нет, уничтожается только один элемент. В любом случае освобождается вся память, занятая массивом.

При обсуждении первоначального варианта языка С++ много спорили о том, должно ли наличие квадратных скобок инициировать поиск или же (как было в исходной спецификации) лучше поручить программисту явно указывать размер массива:

// в первоначальном варианте языка размер массива требовалось задавать явно

delete p[10] parray;

Как вы думаете, почему язык был изменен таким образом, что явного задания размера не требуется (а значит, нужно уметь его сохранять и извлекать), но скобки, хотя и пустые, в операторе delete остались (так что компилятор не должен запоминать, адресует указатель единственный объект или массив)? Какой вариант языка предложили бы вы?


Содержание раздела