Начнем с конструкторов. Очевидно, конструктор без аргументов нам ни к чему — лучше всегда задавать количество аргументов явно, тем самым объявляя массив фиксированной длины. Вторым аргументом этого конструктора может быть значение, которым инициализируются элементы массива. По умолчанию это значение равно нулю.
Реализация конструктора не представляет труда, однако неясным остается важный вопрос: что делать, когда клиент задает неправильный размер, например
нулевой или даже отрицательный? Конечно, ни один программист «в здравом уме и ясной памяти» не напишет отрицательную константу в качестве размера массива, однако длина нашего массива может задаваться с помощью переменной или даже выражения, которое вычисляется во время выполнения и приводится к типу параметра-размера. Поэтому иметь защиту от такой ошибки просто необходимо. Очевидно, что создавать массив в этом случае нельзя, поэтому просто аварийно завершим программу, выдав соответствующее диагностическое сообщение. Аналогично поступим и при реализации других конструкторов. Конечно, это — не лучшее решение, поэтому в главе 7 мы перепишем конструкторы с использованием аппарата обработки исключений. Текст конструктора представлен в листинге 5.3.
Листинг 5.3. Самый простой конструктор
защита "от дурака" размер массива // создали массив
ТАггау::TArray(Uint size, double k)
{ if (size > 0) //
{ size_array = size; //
// //
// неправильная длина
data = new double[size_array]; for(Uint i = 0; i<size_array; ++i) data[i] = k;
else
{ cout << "Not size > 0!"; abort(); }
}
Значение по умолчанию прописано в прототипе конструктора, показанном в интерфейсе класса ТАггау.
Второй конструктор инициализации позволит нам конструировать наш массив из встроенного. Этот конструктор имеет два аргумента-указателя — это обеспечит нам использование любой последовательности элементов исходного встроенного массива для конструирования нашего «умного» (листинг 5.4). Вспомним аналогичный конструктор в классе TString (см. листинг 4.5).
Листинг 5.4. Конструктор инициализации из встроенного массива
// защита "от дурака"
// количество элементов
// создаем массив
// заполняем массив
// копируем из массива
ТАггау::TArray(const double *begin, const double *end) { if (begin < end)
{ size_array = (end - begin); data = new double[size_array]; for(Uint i = 0; i<size_array; ++i) data[i] = *(begin+i);
// неправильные параметры
}
else
{ cout << "Not (begin < end) !"; abort(); }
}
1 Хотя стандарт разрешает задавать нулевой размер динамического массива, в данном случае это, очевидно, явная ошибка.
Нужно отметить, что в этом конструкторе обязательно должно выполняться условие (begin < end) — последний задаваемый элемент не включается. Это позволяет задавать весь массив парой (begin, begin плюс количество). Почему мы именно так трактуем аргументы, поясним на простом примере. Пусть объявлен массив:
double t[10] = {0,1,2,3.4,5,6,7,8.9};
Тогда весь массив t задается парой (t, t+10), а последовательность элементов с i-ro до 7-го, исключая j-и, — парой (t+i. t+j). Такое объявление является естественным в С++ и избавляет нас от возможных ошибок типа «плюс-минус единичка».
Этот конструктор дает возможность объявлять наши «умные» массивы, инициализируя их значениями встроенных массивов, например:
double опе[5] = {5,4,3,2,1};
TArray One (one, one+5); // весь массив
double two[] = {1,2,3,4,5,6};
TArray Two (two, two + sizeof(two)/sizeof(double)); // весь массив
TArray Three (one+1, one+2); // один элемент
TArray Four (&two[l], &two[2]); // один элемент
Массивы One и Two становятся «копиями» массивов one и two соответственно. А вот массив Three содержит только один элемент массива one, равный четырем. Так как параметры конструктора — указатели, то при создании массива можно задавать любые адреса, удовлетворяющие условию (begi n < end). Например, массив Four содержит один элемент массива two, равный двум.
Еще один конструктор позволит нам создавать и инициализировать новый «умный» массив из последовательности элементов уже существующего, что обеспечивается индексом первого элемента последовательности и количеством элементов (листинг 5.5).
Листинг 5.5. Конструктор инициализации из другого «умного» массива
TArray::TArray(const TArray &a. Uint begin, Uint k) { if ((begin<a.size()&&
(k<=a.size()-begin)) // защита от ошибок
{ size_array = k; // количество элементов
data = new double[size_array]; // создаем массив
for(Uint i = 0; i<size__array; ++i) // заполняем массив
data[i] = a.data[i+begin]; // начиная с элемента begin
}
else // неправильные параметры
{ cout << "Not ((0<=begin<size) or (0<k<size)) !"; abort(); }
}
Как обычно, вначале проверяются параметры. Если с ними все в порядке, то создается и заполняется динамический массив.
Конструктор копирования описан далее в разделе «Копирование и присваивание».
При наличии конструкторов можно объявлять «умные» массивы разнообразными способами:
double а[] = {9.1,2,3,22,2.4.7,5};
TArray А(а, a+(sizeof(a)/sizeof(double))); // из массива
TArray В(А); // конструктор копирования
TArray D = В; II конструктор копирования
ТАггау G (В,2,5); ТАггау Н(15); ТАггау F(25. -1);
// вырезка из В
// 15 элементов = 0
// 25 элементов = -1
Массив А инициализируется элементами встроенного массива а. Массивы В и D создаются конструктором копирования. Массив G — это «вырезка» из массива В, в G прописываются 5 элементов В, начиная с В [2]. Пятнадцать элементов массива Н обнуляются, а 25 элементов массива F равны -1.
Как уже отмечалось, параметры конструкторов «умного» массива могут быть переменными, например:
ТАггау::Uint size = (sizeof(a)/sizeof(double))*2; // size = 18
ТАггау H(size); // 18 элементов = 0
ТАггау F(size-8, a[4]); // 10 элементов = 22
ТАггау G(H, 2, size-10); // 8 элементов H
Лучшие программы для веб камер вы всегда можете найти на сайте .
Массив G состоит из восьми элементов массива Н, начиная с Н [2]. Здесь необходимо обратить внимание только на то, что тип Uint, определенный в классе ТАггау, необходимо писать с префиксом класса.
Еще раз нужно подчеркнуть, что в случае неправильных параметров в конструкторах программа заканчивается аварийно, так как обработать ошибки в конструкторах без использования механизма исключений невозможно. Более того, если выделение памяти закончится аварийно, генерируется стандартное исключение типа bad_alloc (см. п. п. 15 и 18.4 в [1]), которое мы тоже не обрабатываем. В этом случае утечки памяти не происходит: память просто не выделяется, и объект не создается. |