Анимация
JavaScript
|
Главная Библионтека if( currtype==„lNT ) { return U.i; } else { cleanupC); currtype= INT; return U.i; } else inline list& myunion::getlist() ifС currtype== LiST ) { return *(reinterpret„cast<LlST*>(U.buff)); } else { cleanupC); list* ptype = newCu.buff) listC); currtype= LiST; return *ptype; } else inline string& MYUNION::getst ri ng C) ifС currtype== STRlNG) { return *Creinterpret cast<STRlNG*>Cu.buff)); } else { cleanupC); STRING* ptype = newCu.buff) STRiNGC); currtype=„STRiNG; return *ptype; } else void MYUNION::cleanupC) switchC currtype ) { case list: { list& ptype = getlistC); )type.~LiSTC); згеак; } case case string: { strings ptype = getstringC); )type.-stringc); згеак; } case default: break; } switch currtype=none; 4. Покажите лучший способ получения обобшенного вариантного типа, и расскажите, с какими сложностями вам пришлось столкнуться. Решение Основные сведения 1. Что такое объединения и для чего они предназначены? Объединения позволяют нескольким объектам, как классам, так и встроенным типам, занимать одно и то же место в памяти. Например: union и { i nt i ; float f; и u; u.i = 42; АКТИВНО поле i std::cout « u.i « std::endl; u.f = 3.14f; Активно поле f std::cout « 2 * u.f « std::endl; Однако в каждый момент времени может быть "активен" только один тип - в конце концов, память может хранить одновременно только одно значение. Кроме того, объединения поддерживают только некоторые виды типов, что приводит нас к следующему вопросу. 2. Какие типы не могут использоваться в качестве членов объединений? Почему существуют такие ограничения? Поясните свой ответ. Из стандарта С++: Объект класса с нетривиальным конструктором, нетривиальным копирующим конструктором, нетривиальным деструктором или нетривиальным оператором копирующего присваивания, или массив объектов такого класса, не может быть членом объединения. Коротко говоря, чтобы класс мог использоваться в объединении, он должен удовлетворять следующим критериям. • Конструкторы, деструкторы и операторы копирующего присваивания должны генерироваться компилятором. • Класс не имеет виртуальных функций или виртуальных базовых классов. • То же должно быть справедливо для всех его базовых классов и нестатических членов. Вот и все, но это ограничивает применение огро.много количества типов. Объединения унаследованы от С. Традиции языка С включают эффективность и поддержку низкоуровневого, приближенного к аппаратному обеспечению программирования, и эти традиции сохранены и в С++ - вот почему в С++ имеются объединения. С другой стороны, у С нет никаких традиций объектной модели с поддержкой классов с конструкторами и деструкторами и пользовательским копированием, что определенно имеется в С++. Вот почему в С++ использование таких новых типов со старыми объединениями, если таковое имеет мссго, не должно нарушать объектную модель С++, включая гарантии времени жизни объектов. Если бы в С++ не было указанных ограничений, могли бы происходить Ужасные Вещи. Рассмотрим, например, что могло бы произойти, если бы был разрешен следующий код. пример 36-2: Этот код не соответствует стандарту С++, но что произошло бы, если бы он был разрешен? void f() { union lllegal Immoral AndFattening { std::string s; std::auto ptr<int> p; ll1egalimmoralAndFattening iiaf; iiaf.s = "Hello, world"; Вызывать ли конструктор s? iiaf.p = new i nt(4); вызывать ли конструктор p? } Будет ли объект s уничтожен? должен ли он уничтожаться? А объект р? Как указывают приведенные комментарии, если бы такой код был разрешен, серьезных проблем не удалось бы избежать. Чтобы избежать ненужного усложнения языка, проблематичные операции были просто запрещены. Но не думайте, что объединения -- просто рудимент. Наиболее полезны объединения, пожалуй, для экономии памяти, поскольку позволяют данным перекрываться, и это остается немалым преимуществом С++ даже сегодня. Например, некоторые из наиболее продвинутых реализаций стандартной библиотеки С++ используют их для "оптимизации малых строк", которая позволяет повторно использовать память внутри самого объекта. Вот основная идея этой оптимизации: в случае строк большого размера объект хранит обычный указатель на динамически выделенную память и дополнительную информацию, такую как, например, размер буфера. Однако эту память, использующуюся для указателя и дополнительной информации, в случае малых строк можно использовать непосредственно для хранения строки, избегая таким образом динамического распределения памяти. Дополнительную информацию об оптимизации малых строк (и других способах оптимизации) можно найти в Suttcrt)2) (задачи 7.2-7.5 в русском издании); см. также обсуждение современных коммерческих реализаций std: :string в [MeyersOl]. Построение объединений 3. В статье [Мап1еу02] рассматривается написание языка сценариев, в котором используются объединения. Допустим, что вы хотите, чтобы ваш язык поддерживал единый тип для переменных, которые в разные моменты времени могут хранить целые числа, строки или списки. Создание union {int i; list<int> 1; string s;}; не работает по причинам, которые разбираются в вопросах 1 и 2. Приведенный далее код представляет собой обходной путь, который пытается обеспечить поддержку произвольного типа в качестве участника объединения (более подробную информацию по этому вопросу вы сможете найти в указанной статье.) С одной стороны, в цитированной статье автором успешно решена реальная проблема. К сожалению, с кодом дела обстоят не вполне благополучно. Проблемы, свя занные с исходным текстом в статье, можно разбить на три основные категории: законность, безопасность и мораль. Отрецензируйте предложенный код и найдите: а) механические ошибки, такие как неверный синтаксис или непереносимые соглашения; б) стилистические улучшения, которые могут повысить ясность исходного текста, его повторное использование и сопровождение. Первый комментарий, который следует сделать по поводу приведенного фрагмента, заключается в том, что основная идея, лежащая в основе данного решения, не законна с точки зрения стандарта С+ + . Вот как описана основная идея в статье: Идея заключается в том, что вместо объявления членов объекта мы объявляем просто буфер {не динамически, а в виде массива типа char в объекте, который должен выступать в роли объединения] и создаем необходимые объекты на лету [путем размещающего конструирования]. - [Мап1еу02] Идея распространенная, но, к сожалению, нездоровая. Выделение буфера одного типа с последующим использованием сго для размещения объекта другого типа не соответствует стандарту и не переносимо, поскольку для буфера, который не выделен динамически (т.е. не выделен при помощи функции mall ос или оператора new), не гарантируется корректное выравнивание для любого другого типа, кроме изначально объявленного. Даже если эта методика случайно и 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 [ 71 ] 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |