Б. Страуструп - Язык программирования С++. Специальное издание, 3-изд. Бином. 2004 (1160791), страница 64
Текст из файла (страница 64)
Такой объект существует до тех пор, пока к указателю на него не будет применен оператор г(е!е1е. Рассмотрим пример: сп1 тат () ( ТаЫе*р = пев ТаЫе; Как это и бывает в большинстве случаев, копирующий конструктор и копирующее прпсваивание значительно отличаются. Основная причина этого сосз опт в том, что копирующий конструктор инициализирует «чистую» (непнициализпрованную) память, в то время как копирующий оператор присваивания должен правильно работать с уже созданным ооъектом. В некоторых случаях присванвание может быть оп гнмизировано, но основная стратегия прн реализации оператора присваивания проста: защита от присваивания самому себе, удаление старых элементов, инициализация и копирование новых элементов.
Как правило, все нестатическпе члены должны быть скопированы (6 10А.63). Для создания отчета об ошибке копирования могут быть использованы иск!печения Я 144,6,2). Методику создания операций копирования, безопасных при исключениях, см. в 9 Д.З.З. Глава 1О. Классы 294 ТпЫе" д = пеш ТаЫе, Не(еее р, е(е(еге р; ЕЕ вероятно визовет о~иибкуво вредя ив полнения Конструктор ТаЫесТаЬЕе () вызываетс я дважды, так же как н деструктор ТаЫес-Та61е (), К сожалению, операторы пеш н гЕе(еге в примере не соответствуют друг другу, поэтому объект, ца который указывает р, удаляется дважды, а объект, на который указывает д, не удаляется вовсе. Неудалепие объекта обычно не является ошибкой с точки зрения языка — это просто потеря памяти. Однако если предполагается, что программа будет выполняться в течение длительного времени, такие потери памяти являются серьезнымц и трудно отлавливаемымп ошибками.
Сугцествуют средства для обнаружения подобных утечек памяти. Удаленно р дважды является осрьезной ошибкой . последствия не определены и, скорее всего, катастрофичны. 1!екоторые реаиизацпи С+ е автоматически освобождают память, занимаемую недоступными из программы объектами (то есть реализуют сборщики мусора), но пх поведение не стандартизовано. Даже прп наличии сборщика мусора, е(еЕеге вызовет деструктор, если таковой определен, поэтому удаление объекта дважлы остается серьезной ошибкой. Во многих случаях это может быть и просто мелким неудобством. В частности, когда известно, что сборщик мусора существует, десгрукторы, осуществляющие лишь упр валею ~с памятью, могут быть опущены, Такое упрощение достигается за счет потери переносимости, а для пскоторых программ и за счет возможного увеличения врсменн выполнения и потери предсказуемости повеления (9 В.9.1'). После применения оператора с(е!еге к объекту любая попытка обращения к этому ооьекту является ошибкой.
К сожалению, реализации не могут надежно обнаруживать такие ошибки. Пользователь может сам определить, каким образом пеш осуществляет выделение памяти н каким образом гЕеЕе(е ее освобождает (см. 9 6.2.6.2 и 9 ! 5.6). Кроме того, можно указать способы взаимодействия выделения памяти, инициализации (конструирования) и исключений (см. 9 !4А.5 и 9 !9А.5).
Массивы в свободной памяти обсуждаются в 9 !ОА.7. 10.4.6. Объекты в качестве членов Рассмотрим класс, который можно использовать для хранения информации о не- большой организации: сЕаев СЕиЬ ( вгг(пя лате; ТаЫе тегпЬегв; ТаЫе одЕЕсегв, ЕЕаееуоипгЕегЕ; ОСЕиЬ (еопвг веппдй и, ЕэагеЯ; В Конструктор СЕиЬ в качестве аргументов получает имя клуба н дату основания. Лргументы конструкторов-членов указываются в списке пнициализапип членов в определении конструктора объемлющего класса.
Например; 295 10.4. Объекты С1иЬ: С1иЬ )сппе! е!»1ггсгй и, Ра!е)г?) . пагл» )и), гпегпЬесе )), о(!гессе )),Уоипс(ес( )(гт) 0- ) В на гале списка ппнцггалпзацпи членов стоит двоеточие, цннцпализаторы отлельных членов разделены запятыми. Конструкторы членов вызываются до вызова конструктора самого класса. Конструкторы вызываются в том порядке, в котором онп объявлены в классе, а не в тол!, в котором они записаны в списке инициализашпг. Во избежание путаницы, лучше записывать список в порядке, соответствующем объявлению. Деструкторы членов вызываются в порядке, обратном вызовам конструкторов после того, как будет выполнено тело деструктора класса. Если конструктор члена не нуждается в аргументах, член можно не указывать в списке ннипиалпзацпп: СгиЬ.
С1иЬ )соле! е!с!пд$ и, Рп!»Я . ппте )п),(оггпнгег! ф1) Л'... ) Это эквивалентно предыдущей версии. В каждом пз этих случаев член С?ибсоЯ»еге создается конструктором ТаЫес Таб(е с аргументом по умолчанию, равным 15. Когда уничтожается объект класса, содержащий объекты классов, сначала вызывается деструктор (ес.ли таковой имеется) класса, а потом деструкторы объектов- членов в порядке, ооратном порядку объявления.
Конструктор создает окружение исполнения для функций-членов снизу вверх (сначала члены). Деструктор уничтожает его сверху вниз (члепы последппмп). 10.4.6.1. Необходимая инициализация членов И !шц нализ аторы членов имеют болыпос значение для типов, у которых инициализацияцпя отличается от присваивания — то есть для объектов-членов классов без конструкторов по умолчанию, для константных членов и для членов, являющихся ссылками. ?? агг р имер: с(аееХ( сове!1п11; С?пЬ с; С1иЬ! рс; 0 Х)!я!!С »оп»ге!г!пДй п,Ра1е!(, С!иЬЙ сгг. г )й), с)п г!) Рс)с) () Не супгествует никакого другого способа инициализации таких членов, и отсутствие инициализации объектов таких типов является ошибкой, Однако для большинства типов программист имеет выбор между использованием иннциализатора и присваиванием.
В этом случае я предпо штаю воспользоваться синтаксисом инициализатора члена, делая таким образом явным тот факт, что инициализация произведена. с?асто инициализация являешься и более аффективной, Например: Глава 10. Классы 296 с1азз Реглан ( ь !с!па пате; е1г!пд аде/геез; //- Регеоп (сопзгРегеопй(; Регзоп (сопл! я!Паяй и, сопл! з!паяй а) РегзопгРегеоп (сопз1е!ггпдй и, сопл! е1ппай а( : пате (и( аг/г1гезз = а; В этом примере пате инициализируется копией п. С другой стороны, асггггезз снача- ла пнипиализируется пустой строкой, а затем ей присваивается копия а.
10.4.6.2. Члены-константы Можно проиннцнализировать член, являющийся статической константой интеграль- ного гппа, добавив к объявлению члена константное еыразкеппа в качестве инициа- лизиругощего значения. Например: Если (п только если) вы используете инициализированный член таким образом, что требуется хранить его в памяти, как объект, член должен быть где-нибудь (одпн раз) определен. Инг!циапизатор не может повторяться: сопи! т1 Сил оиа:с1; // обязан!елька, но не гюеторяйте здесь пнипиа игзатор сопя!т!'р=йСипоикс1; //правильно; Спдопепс! был определен С другой стороны, вы можете использовать элементы перечисления Я 4.8.
Э 14 гпб, (1 15.3) в качестве символической константы внутри определения класса. Например: с1азз Х( епит ( с1 = 7, с2 =- 11, сЗ = 1З, с4 = 1 71; О- В этом случае у вас не возникает искушения объявить переменные, числа с плаваю- щей точкой и т. д. внутри класса. 10.4.6.3. Копирование членов Копирующие конструкторы по умолчанию и копирутощие операторы присваивания по умолчанию Я ! 0,4.4.1) копируют все элементы класса, Если такое копирование не может быть выполнено, попытка копирования объекта класса явчяется ош!лбкой.
Например: с1аке Сипоиз ( з1абс сопя! т1 сг = 7; таис т1 с2 =!1; сопя! т! сЗ = 1З; е!а 1сс сон я! гп! с4 =Д17(; з1а1!с сопе1фоа! сб = 7.0, // араеильно, но аолните об оаределешш //оптбка: не константа // ошибка: не пнатияеская константа //ошиока: пниииализатор неконстангпа //ошибка: пе интегральный тип 297 10.4. Объекты с!аяя !)пщие )ганс!!е [ рпиа1е.
// опериции копирования закрыпгы с целью 0 предотвращения копирования Я ! ! 2,2) !)и!ггие /ганг!!е [сопе1 !)пгс!ие )ганг!!е&], Иг!г)ие )гапс!!е& орега1ог= [сопя117пщие )гапг!!е&! риЫгс вггисг У[ Уп!г!ие )ганг[!е а;,г/ пгребрет явной инициализации //ошибка: невозлгожно скогтровать У:га Кроме того, присваивагпле по умолчанию не может бы сгенерировано, если нестатпческий член является ссылкой, константой или типом, определяемым пользователем, не имеющим копирующего оператора присваивания.
Обратите внимание, что в результате работы копирующего конструктора по умолчанию, член, являющийся ссылкой, в обеих копиях ссылается на один и тот же объект. Это может привести к проблеме, если объект, на который он ссылается, предполагается удалить, При написании копнруюгцего конструктора мы должны аккуратно скопировать все элементы, которые действительно надлежит скопировать. Элементы инициализируются по умолчанию, но очень часто это не то, что требуется в копирующем конструкторе. Например: // оспгороасно! Регяоп. Регяоп [сопя! Регяоп& ар пате [а пате] [] В этом примере я забыл скопировать агй!геев, поэтому ай6езз инициализируется по умолчанию пустой строкой. Прн добавлении нового члена класса всегда проверяйте, не надо ли модифицировать определяемые пользователем конструкторы с учетолл инициализации и копирования нового члена.
10.4.7. Массивы Если объект класса может быть создан без явного задания иницнализиругощего зна- чения, то можно определить массив элементов этого класса. Например: ТаЫе 1Ы[10]; В результате будет создан массив нз десяти объектов типа ТаЫе, и каждый элемент будет проинициалпзглрован прн гюмощи вызова конструктора ТаЫесТаЫе [] с аргуллентом по умолчанию равным !5. Не существует способа явного указания аргументов конструктора (за исключением использования списка инициализации (9 5.2.1, 5 18,6.7)) при объявлении массглва.
Если вам соверщенно необходимо проипицналнзнровать члены массива различными зна ~синями, вы можете написать конструктор по умолчанию, который непосредственно нли косвенно считывает и записывает нелокальные данные. Например; Глава 10.
Классы 299 е!азз 1ЬиЯег ( з1ггад ЬиЯ риЫ1е: ТЬиЯег() (с!а» ЬиЯ) О... ), оо!ОЯ !ЬиЯег звогдв(!00); //" // каждое слово инициализируется из ст ТаЫе' 1! = ТаЫР* 12 = ТаЫе" И = ТаЫе* 14 = лет ТаЫе; ато ТаЫе(ве). аеш ТаЫе, лет ТаЫе(зе(; 0 правильно 0 нравилию // не про в~ыьн о О неправильно е(е!е1е 11; с(е!е1е(] 12 де!еге() 12 с!е!еге 14, То, как на самом деле выделяется память под массивы п отдельные объекты, зависит от реализации.