C++ для начинающих - страница 15
>
>// инициализация элементов массива
>for (int ix=0; ix_size; ++ix)
>ia[ix] = 0;
>}
Это определение содержит несколько упрощенный вариант реализации. Мы не позаботились о том, чтобы попытаться избежать возможных ошибок во время выполнения. Какие ошибки возможны? Во-первых, оператор new может потерпеть неудачу при выделении нужной памяти: в реальной жизни память не бесконечна. (В разделе 2.6 мы увидим, как обрабатываются подобные ситуации.) А во-вторых, параметр sz из-за небрежности программиста может иметь некорректное значение, например нуль или отрицательное.
Что необычного мы видим в таком определении конструктора? Сразу бросается в глаза первая строчка, в которой использована операция разрешения области видимости (::):
>IntArray::IntArray(int sz);
Дело в том, что мы определяем нашу функцию-член (в данном случае конструктор) вне тела класса. Для того чтобы показать, что эта функция на самом деле является членом класса IntArray, мы должны явно предварить имя функции именем класса и двойным двоеточием. (Подробно области видимости разбираются в главе 8; области видимости применительно к классам рассматриваются в разделе 13.9.)
Второй конструктор класса IntArray инициализирует объект IntArray значениями элементов массива встроенного типа. Он требует двух параметров: массива встроенного типа со значениями для инициализации и размера этого массива. Вот как может выглядеть создание объекта IntArray с использованием данного конструктора:
>int ia[10] = {0,1,2,3,4,5,6,7,8,9};
>IntArray iA3(ia,10);
Реализация второго конструктора очень мало отличается от реализации конструктора по умолчанию. (Как и в первом случае, мы пока опустили обработку ошибочных ситуаций.)
>IntArray::IntArray (int *array, int sz)
>{
>// инициализация членов данных
>_size = sz;
>ia = new int[_size];
>// инициализация элементов массива
>for (int ix=0; ix_size; ++ix)
>ia[ix] = array[ix];
>}
Третий конструктор называется копирующим конструктором. Он инициализирует один объект типа IntArray значением другого объекта IntArray. Такой конструктор вызывается автоматически при выполнении следующих инструкций:
>IntArray array;
>// следующие два объявления совершенно эквивалентны:
>IntArray ia1 = array;
>IntArray ia2 (array);
Вот как выглядит реализация копирующего конструктора для IntArray, опять-таки без обработки ошибок:
>IntArray::IntArray (const IntArray rhs )
>{
>// инициализация членов данных
>_size = rhs._size;
>ia = new int[_size];
>// инициализация элементов массива
>for (int ix=0; ix_size; ++ix)
>ia[ix] = rhs.ia[ix];
>}
В этом примере мы видим еще один составной тип данных – ссылку на объект, которая обозначается символом . Ссылку можно рассматривать как разновидность указателя: она также позволяет косвенно обращаться к объекту. Однако синтаксис их использования различается: для доступа к члену объекта, на который у нас есть ссылка, следует использовать точку, а не стрелку; следовательно, мы пишем rhs._size, а не rhs-_size. (Ссылки рассматриваются в разделе 3.6.)
Заметим, что реализация всех трех конструкторов очень похожа. Если один и тот же код повторяется в разных местах, желательно вынести его в отдельную функцию. Это облегчает и дальнейшую модификацию кода, и чтение программы. Вот как можно модернизировать наши конструкторы, если выделить повторяющийся код в отдельную функцию init():
>class IntArray {
>public:
>explicit IntArray (int sz = DefaultArraySize);
>IntArray (int *array, int array_size);
>IntArray (const IntArray rhs);
>// ...
>private:
>void init (int sz,int *array);
>// ...
>};
>// функция, используемая всеми конструкторами
>void IntArray::init (int sz,int *array)
>{
>_size = sz;
>ia = new int[_size];
>for (int ix=0; ix_size; ++ix)
>if ( !array )
>ia[ix] = 0;
>else
>ix[ix] = array[ix];
>}
>// модифицированные конструкторы
>IntArray::IntArray (int sz) { init(sz,0); }
>IntArray::IntArray (int *array, int array_size)
>{ init (array_size,array); }
>IntArray::IntArray (const IntArray rhs)
>{ init (rhs._size,rhs.ia); }
Имеется еще одна специальная функция-член – деструктор, который автоматически вызывается в тот момент, когда объект прекращает существование. Имя деструктора совпадает с именем класса, только в начале идет символ тильды (~). Основное назначение данной функции – освободить ресурсы, отведенные объекту во время его создания и использования. Применение деструкторов помогает бороться с трудно обнаруживаемыми ошибками, ведущими к утечке памяти и других ресурсов. В случае класса IntArray эта функция-член должна освободить память, выделенную в момент создания объекта. (Подробно конструкторы и деструкторы описаны в главе 14.) Вот как выглядит деструктор для IntArray: