И.А. Волкова, А.В. Иванов, Л.Е. Карпов - Основы объектно-ориентированного программирования. Язык программирования С++ (ЧБ) (1114895), страница 6
Текст из файла (страница 6)
Выделение в отдельную группу конструкторов с двумя и более параметрами, независимо от их типа, является в некотором смысле, условным. Так, например, если есть два класса: Vector и Matrix, то для создания соответствующихобъектов:Vector v1(10);Matrix m1(10,15);используется в первом случае один параметр, а во втором случае – два параметра.Таким образом, в первом случае объект создается с помощью конструктора преобразования, а во втором случае, с формальной точки зрения, с помощью конструктора с двумя параметрами, хотя в обоих случаях фактически выполняется однаи та же процедура: создание объекта на основе заданных числовых параметров.Как уже было отмечено, если у параметра конструктора преобразованияимеется априорное значение, и при описании объекта явно не задается фактический параметр, этот конструктор играет роль конструктора умолчания.Пример:class X {int x1;public:X(int px1 = 0}};Для такого класса будут верны следующие объявления объектов:int main(){… Х x1, x2(1); …}4.3.Конструктор копированияПри создании объекта его информационные члены могут быть проинициализированы значениями полей другого объекта этого же типа, то есть объектсоздается как копия другого объекта.Для такого создания объекта используется конструктор копирования.Инициализация может быть выполнена аналогично инициализации переменных встроенных типов с использованием операции присваивания совместно собъявлением объекта:box b5(2,4,6); // создание объекта типа box с// использованием числовых данныхbox b6 = b5; // создание объекта b6 – копии объекта b5Если инициализация производится объектом такого же типа, то объект-инициализатор также может быть указан в круглых скобках после идентификатора создаваемого объекта:box b7(b5);26Свод ситуаций, в которых используется конструктор копирования, описаныниже.Если класс не предусматривает создания внутренних динамических структур, например, массивов, создаваемых с использованием операции new, то вконструкторе копирования достаточно предусмотреть поверхностное копирование, то есть почленное копирование информационных членов класса.Конструктор копирования, осуществляющий поверхностное копирование,можно явно не описывать, он сгенерируется автоматически.Если же в классе предусмотрено создание внутренних динамическихструктур, использование только поверхностного копирования будет ошибочным,так как информационные члены-указатели, находящиеся в разных объектах, будут иметь одинаковые значения и указывать на одну и ту же размещенную в динамической памяти структуру.
Автоматически сгенерированный конструкторакопирования в данном классе не позволит корректно создавать объекты такоготипа на основе других объектов этого же типа.В подобных случаях необходимо глубокое копирование, осуществляющеене только копирование информационных членов объекта, но и самих динамических структур.
При этом, естественно, информационные члены-указатели в создаваемом объекте должны не механически копироваться из объекта-инициализатора, а указывать на вновь созданные динамические структурынового объекта.Пример: Для класса stack конструктор копирования может быть определен следующим образом:class stack {char* c1;int top, size;public:stack(int n = 10){size = n;top = 0;c1 = new char[size];}stack(stack & s1);. . .};stack::stack(stack & s1){size = s1.size;top = s1.top;c1 = new char[size];for (int i = 0; i < size; i++)c1[i] = s1.c1[i];}Замечания по работе конструктора копирования:1) Входной параметр является внешним объектом по отношению к созда27ваемому объекту.
Тем не менее, имеется возможность прямого обращения к закрытым членам этого внешнего объекта. Это возможно толькопотому, что входной параметр имеет тип, совпадающий с типом создаваемого в результате работы конструктора копирования объекта. Еслибы на вход конструктора поступал бы объект другого типа (например, вконструкторе преобразования класса vector входным параметром былбы объект, созданный на основе класса matrix), то для доступа к закрытым членам объекта-параметра необходимо было бы применятьспециальные средства. Это связано с тем, что единицей защиты является не объект, а тип, то есть методы объекта могут обращаться к закрытым членам не только данного объекта, но и к закрытым членамлюбого объекта данного типа.2) В момент описания конструктора копирования класс, как тип данных,еще не описан до конца.
Тем не менее, идентификатор класса уже используется в качестве полноценного типа данных при описании входногопараметра конструктора копирования. Такая технология схожа с описанием рекурсивной функции, когда тело описываемой функции содержит вызов этой же функции.В отличие от конструктора преобразования, входной параметр конструкторакопирования имеет тип, описываемый данным классом. Таким образом, еслиописывается класс Х, то его конструктор копирования может иметь один из следующих прототипов:Х(Х&);Х(const Х&);Объект, создаваемый с использованием конструктора копирования, можетинициализироваться не только именованными объектами, но и временно созданными объектами.Пример:box* b4 = new box(2,3,5); // Явный запуск конструктора// с тремя параметрами.
Адрес// динамически созданного// объекта типа box// присваивается переменной// b4.box b5 = * b4;// Разыменование указателя на объект,// т.е. получение доступа к// информации, хранящейся в нем, и// использование ее для инициализации// создаваемого объекта.box b6 = box(4,7,1); // Создание временного объекта и// инициализация именованного// объекта.284.4.Спецификатор explicitЕсли инициализация создаваемого объекта производится объектом другоготипа, то автоматически производится вызов соответствующего конструкторапреобразования для преобразования инициализирующего значения к типу объявленного объекта.Например, для приведенного выше класса stack описание объекта класса синициализацией может быть таким:stack st1 = 15;оно эквивалентно следующему:stack st1 = stack(15);Таким образом, если пользователь класса stack предполагал создать объектst1 типа stack с максимальной глубиной, задаваемой по умолчанию (10 элементов), и поместить в его вершину значение 15, то он ошибся, поскольку реальным результатом сделанного объявления будет создание пустого стека смаксимальной глубиной в 15 элементов.Для подавления неявного вызова конструктора преобразования, если такоедействие может привести к ошибке, конструктор преобразования необходимообъявлять с использованием ключевого слова explicit (явный):explicit stack(int n = 10){.
. .}В этом случае при объявлении переменной:stack st1 = 15;будет выдана ошибка компиляции: невозможно преобразовать целочисленноезначение в тип класса stack.4.5.Конструктор копирования и операция присваиванияЕсли объект уже создан, то операция присваивания ‘=’ осуществляет неинициализацию создаваемого объекта, а копирование данных, то есть передачуданных между существующими объектами.Пример:box b3(4,1,1); // создание объекта b3box b2;// создание объекта b2b2 = b3;// операция присваивания: копирование объекта// b3 в существующий объект b2.В операции присваивания, так же, как и в конструкторе копирования, поумолчанию осуществляется поверхностное копирование.
Если требуется глубокое копирование, то необходимо перегрузить (описать нужный алгоритм)операцию присваивания. Перегрузка операций рассматривается в следующихразделах.294.6.Автоматическая генерация конструкторов и деструкторовАвтоматически могут генерироваться только конструкторы умолчания,конструкторы копирования и деструкторы.Если в классе явно не описано ни одного конструктора, то автоматическигенерируется конструктор умолчания с пустым телом.Если в классе явно описан хотя бы один конструктор, например, конструкторкопирования, то конструктор умолчания не будет автоматически генерироваться,даже, если он необходим в соответствии с постановкой задачи.В случае отсутствия в классе явно описанного конструктора копирования онвсегда генерируется автоматически и обеспечивает поверхностное копирование.Если в классе не описан деструктор, то всегда автоматически генерируется деструктор, который не производит никаких действий.Таким образом, даже если в классе не описаны конструкторы и деструктор,они все равно неявно присутствуют в нем.4.7.Список инициализацииПри инициализации информационных членов класса, являющихся объектами, создаваемыми на основе других классов, вызываются их конструкторыумолчания.
Если внутренние объекты должны быть созданы с использованиемпараметров (то есть должны быть использованы конструктор преобразования,копирования или конструктор с двумя и более параметрами), то для этого можетбыть использован список инициализации. В списке инициализации также могутбыть инициализированы внутренние объекты встроенных типов.Список инициализации указывается в описании конструктора основногокласса.Прототип конструктора со списком инициализации:Имя_класса ( параметры ) : список_инициализации { тело_класса }Список инициализации – последовательность разделенных запятой инициализируемых переменных встроенных типов и конструкторов для переменных,представляющих объекты типов описанных классов (пользовательских типовданных), а также необходимых конструкторов преобразования или конструкторовс двумя и более параметрами для базовых классов, от которых унаследованописываемый класс.В качестве инициализаторов переменных встроенных типов и параметровконструкторов могут выступать формальные параметры конструктора описываемого класса.