|
Программирование >> Аргументация конструирования
При отладке такой порядок может привести к неприятностям. Некоторые отладчики пытаются выполнить весь код, который находится до main О, и только потом передать управление пользователю. Это прекрасно подходит для С, поскольку до входа в функцию main() там не может быть никакого кода, написанного пользователем. Однако в C++ это может стать причиной большой головной боли, поскольку тела конструкторов для всех глобальных объектов к моменту передачи управления main() уже выполнены. Если хоть один из этих конструкторов содержит серьезный жучок , программа погибнет до того, как начнет выполняться! Существует несколько подходов к решению этой проблемы. Первый заключается в том, чтобы проверять каждый конструктор на локальных объектах перед тем, как использовать его для глобальных. Если это не поможет решить проблему, можно попытаться добавить команды вывода сообщений в начало всех конструкторов, которые, по вашему предположению, могут иметь ошибки. Последнее сообщение, которое вы увидите, вероятно, будет сообщением конструктора с ошибкой. Порядок создания глобальных объектов не определен Локальные объекты создаются в порядке выполнения программы. Для глобальных же объектов порядок создания не определен. Как вы помните, глобальные объекты входят в область видимости программы одновременно. Возникает вопрос: почему бы тогда компилятору не начать с начала файла с исходной программой и не создавать глобальные объекты в порядке их объявления? (Честно говоря, я подозреваю, что на самом деле большинство компиляторов так и поступают.) Увы, такой подход отлично работал бы, но только в том случае, если бы программа всегда состояла из одного файла. Однако большинство программ в реальном мире состоят из нескольких файлов, которые компилируются каждый в отдельности, а уже затем связываются в единое целое. Поскольку компилятор не управляет порядком связывания, он не может влиять на порядок вызова конструкторов глобальных объектов в разных файлах. В принципе в большинстве случаев порядок создания глобальных объектов не так уж и важен. Тем не менее иногда это может привести к ошибкам, которые потом очень сложно отследить (такое случается довольно часто, чтобы обратить на это внимание в книге). Разберем приведенный ниже пример. в файле Student.Н: class Student public: Student (unsigned id) : studentld(id) const unsigned Studentld; class Tutor { public; Tutor (Students s) { tutoredid = E.studentld; protected: tutoredld; b файле FILEl.CPP Создаем студента Student randy(123 4) ; в файле FILE2.CPP Назначаем студенту учителя Tutor jenny(randy); В этом примере конструктор student присваивает студенту идентификатор, а конструктор класса Tutor записывает этот идентификатор студента, которому нужен учитель. Программа объявляет студента randy, а затем назначает ему учителя jenny. При этом подразумевается, что randy создается раньше, чем jenny; в этом-то и состоит проблема. Представьте себе, что порядок создания этих объектов будет другим. Тогда объект jenny будет построен с использованием блока памяти, который пока что не является объектом типа student, а значит, вместо идентификатора студента в randy будет находиться непредсказуемое значение. Приведенный выше пример несложен и несколько надуман. Однако проблемы, создаваемые глобальными объектами, могут оказаться гораздо коварнее. Во избежание этого не допускайте, чтобы конструктор глобального объекта обращался к другому глобальному объекту. Члены создаются в порядке их объявления Члены класса создаются в соответствии с порядком, в котором они объявлены внутри класса. Это не так просто и очевидно, как может показаться на первый взгляд. Рассмотрим пример. class Student public: Student (unsigned id, unsigned age) : sAge(age), sld(id) const unsigned sId; const unsigned sAge; В этом примера s Id создается до sAge, несмотря на то что он стоит вторым в инициализирующем списке конструктора. Впрочем, единственный случай, когда можно заметить какую-то разницу в порядке конструирования, - это когда оба члена класса имеют конструкторы, которым присуще какое-либо общее побочное действие. Деструкторы удаляют объекты в порядке, обратном порядку их создания В каком бы порядке не вызывались конструкторы объектов, вы можете быть уверены, что их деструкторы будут вызваны в обратном порядке. (Приятно сознавать, что хоть одно правило в C++ не имеет никаких или , и либо но ) Глава19 Копирующий конструктор в э>ной главе... Копирование объекта у Автоматический конструктор копирования У Мелкие и глубокие копии У Времеппые объекты опструктор - это специальная функция, которая автоматически вызывается # V/ С++ при создании объекта с тем, чтобы предоставить ему возможность про-инициализировать самого себя. В главе 17, Создание и удаление объектов: конструктор и деструктор , описаны концепции применения конструкторов, в главе 18, Аргументация конструирования , вы познакомились с разными типами конструкторов. А в настоящей главе рассматривается частный случай, известный под названием копирующего конструктора (или конструктора копирования). Конструктор, который используется C++ для создания копий объекта, называется копирующим конструктором. Он имеет вид (xs) (или x::X(cons )), где X - имя класса. Да, это не ошибка - это действительно конструктор класса х, который требует в качестве аргумента ссылку на объект класса X. Я понимаю, что это звучит несколько бессмысленно, но не торопитесь с выводами и позвольте объяснить, зачем такое в C++. Зачем это нужно Подумайте о том, что будет происходить в программе, если вы вызовете следующую функцию: voi udent fs) { тот же сценарий с другими аргументами in t argcs, char* pArgs [ ] ) Student return 0; При вызове описанной функции fn () ей будет передан в качестве аргумента не сам объект, а его копия. Теперь попробуем понять, что же значит - создать копию объекта. Для этого требуется конструктор, который будет создавать объект (даже если копируется уже существующий объект). C++ мог бы побайтово скопировать существующий объект в но-
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |